omni/Expected.h

File members: omni/Expected.h

// Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "detail/ExpectedImpl.h"

namespace omni
{

template <typename TValue, typename TError>
class expected;

template <typename TError>
class unexpected : public omni::detail::UnexpectedImpl<TError>
{
    using base_impl = omni::detail::UnexpectedImpl<TError>;

    template <typename UError>
    friend class unexpected;

public:
    using base_impl::base_impl;

    template <
        typename UError,
        typename IsNoexcept = std::enable_if_t<
            carb::cpp::conjunction<
                std::is_same<TError, UError>,
                typename omni::detail::ExpectedTransformIfNonVoid<carb::cpp::is_swappable, UError, std::true_type>::type>::value,
            typename omni::detail::ExpectedTransformIfNonVoid<carb::cpp::is_nothrow_swappable, UError, std::true_type>::type>>
    constexpr void swap(unexpected& other) noexcept(IsNoexcept::value)
    {
        base_impl::swap(static_cast<base_impl&>(other));
    }

    template <typename UError,
              typename IsNoexcept = std::enable_if_t<
                  omni::detail::ExpectedTransformIfNonVoid<carb::cpp::is_swappable, UError, std::true_type>::type::value,
                  typename omni::detail::ExpectedTransformIfNonVoid<carb::cpp::is_nothrow_swappable, UError, std::true_type>::type>>
    friend constexpr void swap(unexpected& lhs, unexpected<UError>& rhs) noexcept(IsNoexcept::value)
    {
        lhs.swap(rhs);
    }

    template <typename UError>
    constexpr bool operator==(unexpected<UError> const& other) const
    {
        static_assert(std::is_void<TError>::value == std::is_void<UError>::value, "Can not compare void and non-void");

        return base_impl::operator==(other);
    }

    template <typename UError>
    friend constexpr bool operator==(unexpected const& lhs, unexpected<UError> const& rhs)
    {
        return lhs.operator==(rhs);
    }
};

#if CARB_HAS_CPP17

template <typename T>
unexpected(T) -> unexpected<T>;

unexpected()->unexpected<void>;

#endif

struct unexpect_t;

template <typename TValue, typename TError>
class CARB_NODISCARD_TYPE expected : public omni::detail::ExpectedImpl<TValue, TError>
{
private:
    using base_impl = omni::detail::ExpectedImpl<TValue, TError>;

public:
    using value_type = TValue;

    using error_type = TError;

    using unexpected_type = unexpected<error_type>;

    template <typename UValue>
    using rebind = expected<UValue, error_type>;

public:
    using base_impl::base_impl;

    expected() = default;

    expected(expected const& src) = default;

    expected(expected&& src) = default;

    expected& operator=(expected const& src) = default;

    expected& operator=(expected&& src) = default;

    ~expected() = default;

    template <typename UValue = TValue,
              std::enable_if_t<omni::detail::IsExpectedDirectConstructibleFrom<UValue, expected>::is_explicit, bool> = true>
    constexpr explicit expected(UValue&& src) : base_impl(carb::cpp::in_place, std::forward<UValue>(src))
    {
    }

#ifndef DOXYGEN_BUILD
    template <typename UValue = TValue,
              std::enable_if_t<!omni::detail::IsExpectedDirectConstructibleFrom<UValue, expected>::is_explicit, bool> = false>
    constexpr expected(UValue&& src) : base_impl(carb::cpp::in_place, std::forward<UValue>(src))
    {
    }
#endif

    template <typename UValue,
              typename UError,
              std::enable_if_t<omni::detail::IsExpectedCopyConstructibleFrom<expected<UValue, UError>, expected>::is_explicit,
                               bool> = true>
    constexpr explicit expected(expected<UValue, UError> const& src)
        : base_impl(::omni::detail::ExpectedCtorBypass{}, src)
    {
    }

#ifndef DOXYGEN_BUILD
    template <typename UValue,
              typename UError,
              std::enable_if_t<!omni::detail::IsExpectedCopyConstructibleFrom<expected<UValue, UError>, expected>::is_explicit,
                               bool> = false>
    constexpr expected(expected<UValue, UError> const& src) : base_impl(::omni::detail::ExpectedCtorBypass{}, src)
    {
    }
#endif

    template <typename UValue,
              typename UError,
              std::enable_if_t<omni::detail::IsExpectedMoveConstructibleFrom<expected<UValue, UError>, expected>::is_explicit,
                               bool> = true>
    constexpr explicit expected(expected<UValue, UError>&& src)
        : base_impl(::omni::detail::ExpectedCtorBypass{}, std::move(src))
    {
    }

#ifndef DOXYGEN_BUILD
    template <typename UValue,
              typename UError,
              std::enable_if_t<!omni::detail::IsExpectedMoveConstructibleFrom<expected<UValue, UError>, expected>::is_explicit,
                               bool> = false>
    constexpr expected(expected<UValue, UError>&& src) : base_impl(::omni::detail::ExpectedCtorBypass{}, std::move(src))
    {
    }
#endif

    template <typename UError,
              std::enable_if_t<carb::cpp::conjunction<std::is_constructible<TError, UError const&>,
                                                      carb::cpp::negation<std::is_convertible<UError const&, TError>>>::value,
                               bool> = true>
    constexpr explicit expected(unexpected<UError> const& src) : base_impl(unexpect, src.error())
    {
    }

#ifndef DOXYGEN_BUILD
    template <typename UError,
              std::enable_if_t<carb::cpp::conjunction<std::is_constructible<TError, UError const&>,
                                                      std::is_convertible<UError const&, TError>>::value,
                               bool> = false>
    constexpr expected(unexpected<UError> const& src) : base_impl(unexpect, src.error())
    {
    }
#endif

    template <typename UError,
              std::enable_if_t<carb::cpp::conjunction<std::is_constructible<TError, UError&&>,
                                                      carb::cpp::negation<std::is_convertible<UError&&, TError>>>::value,
                               bool> = true>
    constexpr explicit expected(unexpected<UError>&& src) : base_impl(unexpect, std::move(src).error())
    {
    }

#ifndef DOXYGEN_BUILD
    template <typename UError,
              std::enable_if_t<carb::cpp::conjunction<std::is_constructible<TError, UError&&>,
                                                      std::is_convertible<UError&&, TError>>::value,
                               bool> = false>
    constexpr expected(unexpected<UError>&& src) : base_impl(unexpect, std::move(src).error())
    {
    }
#endif

    constexpr bool has_value() const noexcept
    {
        return this->m_state == ::omni::detail::ExpectedState::eSUCCESS;
    }

    constexpr explicit operator bool() const noexcept
    {
        return this->has_value();
    }

#ifdef DOXYGEN_BUILD
    constexpr value_type& value() &;

    constexpr value_type const& value() const&;

    constexpr value_type&& value() &&;

    constexpr value_type const&& value() const&&;

    template <typename UValue>
    constexpr value_type value_or(UValue&& default_value) const&;

    template <typename UValue>
    constexpr value_type value_or(UValue&& default_value) &&;

    constexpr error_type& error() & noexcept;

    constexpr error_type const& error() const& noexcept;

    constexpr error_type&& error() && noexcept;

    constexpr error_type const&& error() const&& noexcept;

    constexpr value_type* operator->() noexcept;

    constexpr value_type const* operator->() const noexcept;

    constexpr value_type& operator*() & noexcept;

    constexpr value_type const& operator*() const& noexcept;

    constexpr value_type&& operator*() && noexcept;

    constexpr value_type const&& operator*() const&& noexcept;

    template <typename... TArgs>
    value_type& emplace(TArgs&&... args) noexcept;

    void emplace() noexcept;
#endif

    template <typename F>
    constexpr auto transform(F&& f) const&
    {
        return ::omni::detail::ExpectedOpTransform<F, expected, expected const&>::transform(std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto transform(F&& f) &
    {
        return ::omni::detail::ExpectedOpTransform<F, expected, expected&>::transform(std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto transform(F&& f) &&
    {
        return ::omni::detail::ExpectedOpTransform<F, expected, expected&&>::transform(
            std::forward<F>(f), std::move(*this));
    }

    template <typename F>
    constexpr auto transform(F&& f) const&&
    {
        return ::omni::detail::ExpectedOpTransform<F, expected, expected const&&>::transform(
            std::forward<F>(f), std::move(*this));
    }

    template <typename F>
    constexpr auto and_then(F&& f) const&
    {
        return ::omni::detail::ExpectedOpAndThen<F, expected, expected const&>::and_then(std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto and_then(F&& f) &
    {
        return ::omni::detail::ExpectedOpAndThen<F, expected, expected&>::and_then(std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto and_then(F&& f) &&
    {
        return ::omni::detail::ExpectedOpAndThen<F, expected, expected&&>::and_then(std::forward<F>(f), std::move(*this));
    }

    template <typename F>
    constexpr auto and_then(F&& f) const&&
    {
        return ::omni::detail::ExpectedOpAndThen<F, expected, expected const&&>::and_then(
            std::forward<F>(f), std::move(*this));
    }

    template <typename F>
    constexpr auto transform_error(F&& f) const&
    {
        return ::omni::detail::ExpectedOpTransformError<F, expected, expected const&>::transform_error(
            std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto transform_error(F&& f) &
    {
        return ::omni::detail::ExpectedOpTransformError<F, expected, expected&>::transform_error(
            std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto transform_error(F&& f) &&
    {
        return ::omni::detail::ExpectedOpTransformError<F, expected, expected&&>::transform_error(
            std::forward<F>(f), std::move(*this));
    }

    template <typename F>
    constexpr auto transform_error(F&& f) const&&
    {
        return ::omni::detail::ExpectedOpTransformError<F, expected, expected const&&>::transform_error(
            std::forward<F>(f), std::move(*this));
    }

    template <typename F>
    constexpr auto or_else(F&& f) const&
    {
        return ::omni::detail::ExpectedOpOrElse<F, expected, expected const&>::or_else(std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto or_else(F&& f) &
    {
        return ::omni::detail::ExpectedOpOrElse<F, expected, expected&>::or_else(std::forward<F>(f), *this);
    }

    template <typename F>
    constexpr auto or_else(F&& f) &&
    {
        return ::omni::detail::ExpectedOpOrElse<F, expected, expected&&>::or_else(std::forward<F>(f), std::move(*this));
    }

    template <typename F>
    constexpr auto or_else(F&& f) const&&
    {
        return ::omni::detail::ExpectedOpOrElse<F, expected, expected const&&>::or_else(
            std::forward<F>(f), std::move(*this));
    }
};

template <typename TValueLhs, typename TErrorLhs, typename TValueRhs, typename TErrorRhs>
constexpr bool operator==(expected<TValueLhs, TErrorLhs> const& lhs, expected<TValueRhs, TErrorRhs> const& rhs)
{
    static_assert(std::is_void<TValueLhs>::value == std::is_void<TValueRhs>::value,
                  "Can not compare expected of non-void `value_type` with one of void `value_type`");
    static_assert(std::is_void<TErrorLhs>::value == std::is_void<TErrorRhs>::value,
                  "Can not compare expected of non-void `error_type` with one of void `error_type`");
    return ::omni::detail::ExpectedComparer<expected<TValueLhs, TErrorLhs>, expected<TValueRhs, TErrorRhs>>::eq(lhs, rhs);
}

template <typename TValueLhs, typename TErrorLhs, typename TValueRhs>
constexpr bool operator==(expected<TValueLhs, TErrorLhs> const& lhs, TValueRhs const& rhs)
{
    static_assert(!std::is_void<TValueLhs>::value, "Can not compare void-valued expected with value");
    return lhs.has_value() && static_cast<bool>(lhs.value() == rhs);
}

template <typename TValueLhs, typename TErrorLhs, typename TErrorRhs>
constexpr bool operator==(expected<TValueLhs, TErrorLhs> const& lhs, unexpected<TErrorRhs> const& rhs)
{
    static_assert(!std::is_void<TErrorLhs>::value && std::is_void<TErrorRhs>::value,
                  "Can not compare expected of non-void `error_type` with unexpected<void>");
    static_assert(std::is_void<TErrorLhs>::value && !std::is_void<TErrorRhs>::value,
                  "Can not compare expected of void `error_type` with unexpected of non-void `error_type`");
    return ::omni::detail::ExpectedUnexpectedComparer<TErrorLhs, TErrorRhs>::eq(lhs, rhs);
}

} // namespace omni