omni/detail/ExpectedImpl.h
File members: omni/detail/ExpectedImpl.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 "../../carb/cpp/Functional.h"
#include "../../carb/cpp/Memory.h"
#include "../../carb/cpp/TypeTraits.h"
#include "../../carb/cpp/Utility.h"
#include "../../carb/cpp/Memory.h"
#include "../../carb/cpp/TypeTraits.h"
#include "../core/IObject.h"
#include "ConvertsFromAnyCvRef.h"
#include "ParamPack.h"
#include <cstddef>
#include <initializer_list>
namespace omni
{
template <typename TValue, typename TError>
class expected;
template <typename TError>
class unexpected;
struct unexpect_t
{
explicit unexpect_t() = default;
};
constexpr unexpect_t unexpect{};
template <typename TError>
class bad_expected_access;
template <>
class bad_expected_access<void> : public std::exception
{
public:
bad_expected_access() = default;
char const* what() const noexcept override
{
return "Invalid access of `omni::expected`";
}
};
template <typename TError>
class bad_expected_access : public bad_expected_access<void>
{
public:
explicit bad_expected_access(TError e) : m_error(std::move(e))
{
}
TError const& error() const& noexcept
{
return m_error;
}
TError& error() & noexcept
{
return m_error;
}
TError const&& error() const&& noexcept
{
return std::move(m_error);
}
TError&& error() && noexcept
{
return std::move(m_error);
}
private:
TError m_error;
};
namespace detail
{
using carb::cpp::bool_constant;
using carb::cpp::conjunction;
using carb::cpp::disjunction;
using carb::cpp::in_place;
using carb::cpp::in_place_t;
using carb::cpp::is_void;
using carb::cpp::negation;
using carb::cpp::remove_cvref_t;
using carb::cpp::type_identity;
using carb::cpp::void_t;
using omni::core::Result;
using std::conditional_t;
using std::enable_if_t;
using std::false_type;
using std::integral_constant;
using std::is_constructible;
using std::is_copy_constructible;
using std::is_move_constructible;
using std::is_nothrow_constructible;
using std::is_nothrow_copy_assignable;
using std::is_nothrow_copy_constructible;
using std::is_nothrow_destructible;
using std::is_nothrow_move_assignable;
using std::is_nothrow_move_constructible;
using std::is_trivially_copy_assignable;
using std::is_trivially_copy_constructible;
using std::is_trivially_destructible;
using std::is_trivially_move_assignable;
using std::is_trivially_move_constructible;
using std::true_type;
// Helpers //
template <typename T>
struct IsExpected : false_type
{
};
template <typename TValue, typename TError>
struct IsExpected<omni::expected<TValue, TError>> : true_type
{
};
template <typename T>
struct IsUnexpected : false_type
{
};
template <typename TError>
struct IsUnexpected<omni::unexpected<TError>> : true_type
{
};
struct ExpectedStorageVoid
{
};
template <typename T>
struct IsValidExpectedValue : conjunction<disjunction<std::is_object<T>, is_void<T>>, negation<std::is_array<T>>>
{
};
template <typename T>
struct IsValidExpectedError : conjunction<disjunction<std::is_object<T>, is_void<T>>,
negation<std::is_array<T>>,
negation<IsUnexpected<T>>,
negation<std::is_const<T>>,
negation<std::is_volatile<T>>>
{
};
template <template <typename...> class TTTransform, typename T, typename TFallback = void, typename = void_t<>>
struct ExpectedTransformIfNonVoid
{
using type = TFallback;
};
template <template <typename...> class TTTransform, typename T, typename TFallback>
struct ExpectedTransformIfNonVoid<TTTransform, T, TFallback, std::enable_if_t<!std::is_void<T>::value>>
{
using type = typename TTTransform<T>::type;
};
template <typename TFromValue,
typename TFromError,
typename TFromValueExpr,
typename TFromErrorExpr,
typename TToValue,
typename TToError,
typename = void_t<>>
struct IsExpectedConvertibleImpl : std::false_type
{
};
template <typename TFromValue, typename TFromError, typename TFromValueExpr, typename TFromErrorExpr, typename TToValue, typename TToError>
struct IsExpectedConvertibleImpl<
TFromValue,
TFromError,
TFromValueExpr,
TFromErrorExpr,
TToValue,
TToError,
std::enable_if_t<conjunction<
// (18.1): is_constructible_v<T, UF> is true; and
disjunction<conjunction<std::is_void<TToValue>, std::is_void<TFromValue>>, std::is_constructible<TToValue, TFromValue>>,
// (18.2): is_constructible_v<E, GF> is true; and
disjunction<conjunction<std::is_void<TToError>, std::is_void<TFromError>>, std::is_constructible<TToError, TFromError>>,
// (18.3): if T is not cv bool, converts-from-any-cvref<T, expected<U, G>> is false; and
disjunction<std::is_same<bool, std::remove_cv_t<TToValue>>,
negation<ConvertsFromAnyCvRef<TToValue, omni::expected<TFromValue, TFromError>>>>,
// (18.4): is_constructible_v<unexpected<E>, expected<U, G>&> is false; and
negation<std::is_constructible<omni::unexpected<TToError>, omni::expected<TFromValue, TFromError>&>>,
// (18.5): is_constructible_v<unexpected<E>, expected<U, G>> is false; and
negation<std::is_constructible<omni::unexpected<TToError>, omni::expected<TFromValue, TFromError>>>,
// (18.6): is_constructible_v<unexpected<E>, const expected<U, G>&> is false; and
negation<std::is_constructible<omni::unexpected<TToError>, omni::expected<TFromValue, TFromError> const&>>,
// (18.7): is_constructible_v<unexpected<E>, const expected<U, G>> is false.
negation<std::is_constructible<omni::unexpected<TToError>, omni::expected<TFromValue, TFromError> const>>>::value>>
: std::true_type
{
static constexpr bool is_explicit = disjunction<negation<std::is_convertible<TFromValueExpr, TToValue>>,
negation<std::is_convertible<TFromErrorExpr, TToError>>>::value;
};
template <typename TFrom, typename TTo, template <typename...> class TTTypeTransformer, typename = void_t<>>
struct IsExpectedConvertible : std::false_type
{
};
template <typename TFromValue, typename TFromError, typename TToValue, typename TToError, template <typename...> class TTTypeTransformer>
struct IsExpectedConvertible<
omni::expected<TFromValue, TFromError>,
omni::expected<TToValue, TToError>,
TTTypeTransformer,
std::enable_if_t<!conjunction<std::is_same<TFromValue, TToValue>, std::is_same<TFromError, TToError>>::value>>
: IsExpectedConvertibleImpl<TFromValue,
TFromError,
typename ExpectedTransformIfNonVoid<TTTypeTransformer, TFromValue>::type,
typename ExpectedTransformIfNonVoid<TTTypeTransformer, TFromError>::type,
TToValue,
TToError>
{
};
template <typename T>
struct AddConstLvalueRef
{
using type = std::add_lvalue_reference_t<std::add_const_t<T>>;
};
template <typename TFrom, typename TTo>
using IsExpectedCopyConstructibleFrom = IsExpectedConvertible<TFrom, TTo, AddConstLvalueRef>;
template <typename TFrom, typename TTo>
using IsExpectedMoveConstructibleFrom = IsExpectedConvertible<TFrom, TTo, type_identity>;
template <typename TFrom, typename TTo, typename = void_t<>>
struct IsExpectedDirectConstructibleFromImpl : std::false_type
{
};
template <typename UValue, typename TValue, typename TError>
struct IsExpectedDirectConstructibleFromImpl<
UValue,
omni::expected<TValue, TError>,
std::enable_if_t<
conjunction<negation<is_void<TValue>>,
negation<std::is_same<in_place_t, remove_cvref_t<UValue>>>,
negation<std::is_same<omni::expected<TValue, TError>, remove_cvref_t<UValue>>>,
is_constructible<TValue, UValue>,
negation<IsUnexpected<remove_cvref_t<UValue>>>,
disjunction<negation<std::is_same<bool, remove_cvref_t<TValue>>>, negation<IsExpected<UValue>>>>::value>>
: std::false_type
{
static constexpr bool is_explicit = !std::is_convertible<UValue, TValue>::value;
};
template <typename TFrom, typename TTo>
struct IsExpectedDirectConstructibleFrom : IsExpectedDirectConstructibleFromImpl<TFrom, TTo>
{
};
struct ExpectedCtorBypass
{
};
#if CARB_EXCEPTIONS_ENABLED
template <typename TOld, typename TNew, typename TParamPack, typename = void_t<>>
struct ExpectedReinitImpl
{
template <typename... TArgs>
static void reinit(TOld* pold, TNew* pnew, TArgs&&... args)
{
TOld temp{ std::move(*pold) };
carb::cpp::destroy_at(pold);
try
{
carb::cpp::construct_at(pnew, std::forward<TArgs>(args)...);
}
catch (...)
{
carb::cpp::construct_at(pold, std::move(temp));
throw;
}
}
};
template <typename TOld, typename TNew, typename... TArgs>
struct ExpectedReinitImpl<TOld, TNew, ParamPack<TArgs...>, std::enable_if_t<std::is_nothrow_constructible<TNew, TArgs...>::value>>
{
static void reinit(TOld* pold, TNew* pnew, TArgs&&... args)
{
carb::cpp::destroy_at(pold);
carb::cpp::construct_at(pnew, std::forward<TArgs>(args)...);
}
};
template <typename TOld, typename TNew, typename... TArgs>
struct ExpectedReinitImpl<TOld,
TNew,
ParamPack<TArgs...>,
std::enable_if_t<conjunction<std::is_nothrow_move_constructible<TNew>,
negation<std::is_nothrow_constructible<TNew, TArgs...>>>::value>>
{
static void reinit(TOld* pold, TNew* pnew, TArgs&&... args)
{
TNew temp{ std::forward<TArgs>(args)... };
carb::cpp::destroy_at(pold);
carb::cpp::construct_at(pnew, std::move(temp));
}
};
#else // !CARB_EXCEPTIONS_ENABLED
template <typename TOld, typename TNew, typename TParamPack>
struct ExpectedReinitImpl
{
template <typename... TArgs>
static void reinit(TOld* pold, TNew* pnew, TArgs&&... args) noexcept
{
carb::cpp::destroy_at(pold);
carb::cpp::construct_at(pnew, std::forward<TArgs>(args)...);
}
};
#endif // CARB_EXCEPTIONS_ENABLED
template <typename TOld, typename TNew, typename... TArgs>
constexpr void expected_reinit(TOld* pold, TNew* pnew, TArgs&&... args)
{
ExpectedReinitImpl<TOld, TNew, ParamPack<TArgs...>>::reinit(pold, pnew, std::forward<TArgs>(args)...);
}
// Unexpected //
template <typename TError>
class UnexpectedImpl
{
static_assert(IsValidExpectedError<TError>::value, "Type is not valid for an expected error type");
template <typename UError>
friend class UnexpectedImpl;
public:
constexpr UnexpectedImpl(UnexpectedImpl const&) = default;
constexpr UnexpectedImpl(UnexpectedImpl&&) = default;
template <typename UError = TError,
typename = std::enable_if_t<conjunction<negation<IsUnexpected<remove_cvref_t<UError>>>,
negation<std::is_same<remove_cvref_t<UError>, in_place_t>>,
is_constructible<TError, UError>>::value>>
constexpr explicit UnexpectedImpl(UError&& src) : m_error{ std::forward<UError>(src) }
{
}
template <typename... TArgs, typename = std::enable_if_t<is_constructible<TError, TArgs...>::value>>
constexpr explicit UnexpectedImpl(in_place_t, TArgs&&... args) : m_error{ std::forward<TArgs>(args)... }
{
}
template <typename U,
typename... TArgs,
typename = std::enable_if_t<is_constructible<TError, std::initializer_list<U>&, TArgs...>::value>>
constexpr explicit UnexpectedImpl(in_place_t, std::initializer_list<U> init_list, TArgs&&... args)
: m_error{ init_list, std::forward<TArgs>(args)... }
{
}
constexpr TError& error() & noexcept
{
return m_error;
}
constexpr TError const& error() const& noexcept
{
return m_error;
}
constexpr TError&& error() && noexcept
{
return std::move(m_error);
}
constexpr TError const&& error() const&& noexcept
{
return std::move(m_error);
}
constexpr void swap(UnexpectedImpl<TError>& other)
{
using std::swap;
swap(m_error, other.m_error);
}
template <typename UError>
constexpr bool operator==(UnexpectedImpl<UError> const& other) const
{
return m_error == other.m_error;
}
protected:
TError m_error;
};
template <>
class UnexpectedImpl<void>
{
public:
constexpr UnexpectedImpl() noexcept
{
}
constexpr explicit UnexpectedImpl(in_place_t) noexcept
{
}
constexpr void error() const noexcept
{
}
constexpr void swap(UnexpectedImpl<void>) noexcept
{
}
constexpr bool operator==(UnexpectedImpl<void>) const noexcept
{
return true;
}
};
[[noreturn]] inline void invalid_expected_access_error() noexcept
{
CARB_FATAL_UNLESS(false, "Call to `omni::expected::error()` on successful instance");
}
#if CARB_EXCEPTIONS_ENABLED
template <typename T>
[[noreturn]] inline enable_if_t<conjunction<negation<std::is_same<std::decay_t<T>, ExpectedStorageVoid>>,
std::is_constructible<std::decay_t<T>, T>>::value>
invalid_expected_access(T&& x)
{
throw ::omni::bad_expected_access<std::decay_t<T>>(std::forward<T>(x));
}
template <typename T>
[[noreturn]] inline enable_if_t<conjunction<negation<std::is_same<std::decay_t<T>, ExpectedStorageVoid>>,
negation<std::is_constructible<std::decay_t<T>, T>>>::value>
invalid_expected_access(T&&)
{
throw ::omni::bad_expected_access<void>();
}
[[noreturn]] inline void invalid_expected_access(ExpectedStorageVoid)
{
throw ::omni::bad_expected_access<void>();
}
#else // !CARB_EXCEPTIONS_ENABLED
template <typename T>
[[noreturn]] inline enable_if_t<std::is_same<std::decay_t<T>, ExpectedStorageVoid>::value> invalid_expected_access(T&&)
{
CARB_FATAL_UNLESS(false, "Invalid access of `omni::expected::value()`");
}
#endif
// Storage //
// This section governs the byte layout of an `omni::excepted` and the trivialness of its special member functions //
// (namely: copy, move, and destructor -- initialization is never trivial). All information relevant to the ABI of //
// `expected` should be defined in this section. //
enum class ExpectedState : std::uint8_t
{
eSUCCESS = 0,
eUNEXPECTED = 1,
};
// clang-format off
template <typename TValue,
typename TError,
bool TrivialDtor = conjunction<disjunction<is_void<TValue>, is_trivially_destructible<TValue>>,
disjunction<is_void<TError>, is_trivially_destructible<TError>>
>::value
>
union ExpectedStorage;
// clang-format on
template <typename TValue, typename TError>
union ExpectedStorage<TValue, TError, /*TrivialDtor=*/true>
{
conditional_t<is_void<TValue>::value, ExpectedStorageVoid, TValue> value;
conditional_t<is_void<TError>::value, ExpectedStorageVoid, TError> error;
ExpectedStorageVoid uninit;
template <typename... TArgs>
constexpr explicit ExpectedStorage(in_place_t, TArgs&&... args) : value{ std::forward<TArgs>(args)... }
{
}
template <typename... TArgs>
constexpr explicit ExpectedStorage(unexpect_t, TArgs&&... args) : error{ std::forward<TArgs>(args)... }
{
}
constexpr explicit ExpectedStorage(ExpectedCtorBypass) : uninit{}
{
}
};
template <typename TValue, typename TError>
union ExpectedStorage<TValue, TError, /*TrivialDtor=*/false>
{
conditional_t<is_void<TValue>::value, ExpectedStorageVoid, TValue> value;
conditional_t<is_void<TError>::value, ExpectedStorageVoid, TError> error;
ExpectedStorageVoid uninit;
template <typename... TArgs>
constexpr explicit ExpectedStorage(in_place_t, TArgs&&... args) : value{ std::forward<TArgs>(args)... }
{
}
template <typename... TArgs>
constexpr explicit ExpectedStorage(unexpect_t, TArgs&&... args) : error{ std::forward<TArgs>(args)... }
{
}
constexpr explicit ExpectedStorage(ExpectedCtorBypass) : uninit{}
{
}
~ExpectedStorage() noexcept
{
// Intentionally empty -- it is the responsibility of the owner to call value or error destructor, since only it
// knows which alternative is active.
}
};
template <typename TValue, typename TError>
class ExpectedImplBase
{
using StoredValueType = conditional_t<is_void<TValue>::value, ExpectedStorageVoid, TValue>;
using StoredErrorType = conditional_t<is_void<TError>::value, ExpectedStorageVoid, TError>;
template <typename UValue, typename UError>
friend class ExpectedImplBase;
protected:
template <typename... TArgs, typename = enable_if_t<is_constructible<StoredValueType, TArgs...>::value>>
explicit ExpectedImplBase(in_place_t,
TArgs&&... args) noexcept(is_nothrow_constructible<StoredValueType, TArgs...>::value)
: m_state{ ExpectedState::eSUCCESS }, m_storage{ in_place, std::forward<TArgs>(args)... }
{
}
template <typename... TArgs, typename = enable_if_t<is_constructible<StoredErrorType, TArgs...>::value>>
explicit ExpectedImplBase(unexpect_t,
TArgs&&... args) noexcept(is_nothrow_constructible<StoredErrorType, TArgs...>::value)
: m_state{ ExpectedState::eUNEXPECTED }, m_storage{ unexpect, std::forward<TArgs>(args)... }
{
}
template <typename UValue, typename UError>
constexpr explicit ExpectedImplBase(ExpectedCtorBypass bypass, ExpectedImplBase<UValue, UError> const& src)
: m_state{ src.m_state }, m_storage{ bypass }
{
if (src.m_state == ExpectedState::eSUCCESS)
carb::cpp::construct_at(carb::cpp::addressof(m_storage.value), src.m_storage.value);
else
carb::cpp::construct_at(carb::cpp::addressof(m_storage.error), src.m_storage.error);
m_state = src.m_state;
}
template <typename UValue, typename UError>
constexpr explicit ExpectedImplBase(ExpectedCtorBypass bypass, ExpectedImplBase<UValue, UError>&& src)
: m_state{ src.m_state }, m_storage{ bypass }
{
if (src.m_state == ExpectedState::eSUCCESS)
carb::cpp::construct_at(carb::cpp::addressof(m_storage.value), std::move(src.m_storage.value));
else
carb::cpp::construct_at(carb::cpp::addressof(m_storage.error), std::move(src.m_storage.error));
m_state = src.m_state;
}
void unsafe_destroy()
{
if (m_state == ExpectedState::eSUCCESS)
carb::cpp::destroy_at(carb::cpp::addressof(m_storage.value));
else
carb::cpp::destroy_at(carb::cpp::addressof(m_storage.error));
}
void copy_from(ExpectedImplBase const& src)
{
if (m_state == src.m_state)
{
if (m_state == ExpectedState::eSUCCESS)
m_storage.value = src.m_storage.value;
else
m_storage.error = src.m_storage.error;
}
else if (m_state == ExpectedState::eSUCCESS) // && src.m_state == ExpectedState::eUNEXPECTED
{
expected_reinit(
carb::cpp::addressof(m_storage.value), carb::cpp::addressof(m_storage.error), src.m_storage.error);
}
else // m_state == ExpectedState::eUNEXPECTED && src.m_state == ExpectedState::eSUCCESS
{
expected_reinit(
carb::cpp::addressof(m_storage.error), carb::cpp::addressof(m_storage.value), src.m_storage.value);
}
m_state = src.m_state;
}
void move_from(ExpectedImplBase&& src)
{
if (m_state == src.m_state)
{
if (m_state == ExpectedState::eSUCCESS)
m_storage.value = std::move(src.m_storage.value);
else
m_storage.error = std::move(src.m_storage.error);
}
else if (m_state == ExpectedState::eSUCCESS) // && src.m_state == ExpectedState::eUNEXPECTED
{
expected_reinit(carb::cpp::addressof(m_storage.value), carb::cpp::addressof(m_storage.error),
std::move(src.m_storage.error));
}
else // m_state == ExpectedState::eUNEXPECTED && src.m_state == ExpectedState::eSUCCESS
{
expected_reinit(carb::cpp::addressof(m_storage.error), carb::cpp::addressof(m_storage.value),
std::move(src.m_storage.value));
}
m_state = src.m_state;
}
protected:
ExpectedState m_state;
ExpectedStorage<TValue, TError> m_storage;
};
template <>
class ExpectedImplBase<void, void>
{
protected:
explicit ExpectedImplBase(in_place_t) noexcept : m_state{ ExpectedState::eSUCCESS }
{
}
explicit ExpectedImplBase(unexpect_t) noexcept : m_state{ ExpectedState::eUNEXPECTED }
{
}
explicit ExpectedImplBase(ExpectedCtorBypass, ExpectedImplBase const& src) noexcept : m_state{ src.m_state }
{
}
constexpr void unsafe_destroy()
{
}
protected:
ExpectedState m_state;
};
enum class ExpectedPayloadTag : std::uint8_t
{
eTRIVIAL,
eCALL_COPY,
eCALL_MOVE,
eCALL_BOTH,
eCALL_DTOR,
};
// clang-format off
template <typename TValue,
typename TError,
bool TrivialCopy = conjunction<disjunction<is_void<TValue>, conjunction<is_trivially_copy_assignable<TValue>, is_trivially_copy_constructible<TValue>>>,
disjunction<is_void<TError>, conjunction<is_trivially_copy_assignable<TError>, is_trivially_copy_constructible<TError>>>
>::value,
bool TrivialMove = conjunction<disjunction<is_void<TValue>, conjunction<is_trivially_move_assignable<TValue>, is_trivially_move_constructible<TValue>>>,
disjunction<is_void<TError>, conjunction<is_trivially_move_assignable<TError>, is_trivially_move_constructible<TError>>>
>::value,
bool TrivialDtor = conjunction<disjunction<is_void<TValue>, is_trivially_destructible<TValue>>,
disjunction<is_void<TError>, is_trivially_destructible<TError>>
>::value
>
struct ExpectedPayloadTagFor;
// clang-format on
template <typename TValue, typename TError>
struct ExpectedPayloadTagFor<TValue, TError, /*TrivialCopy=*/true, /*TrivialMove=*/true, /*TrivialDtor=*/true>
: integral_constant<ExpectedPayloadTag, ExpectedPayloadTag::eTRIVIAL>
{
};
template <typename TValue, typename TError>
struct ExpectedPayloadTagFor<TValue, TError, /*TrivialCopy=*/false, /*TrivialMove=*/true, /*TrivialDtor=*/true>
: integral_constant<ExpectedPayloadTag, ExpectedPayloadTag::eCALL_COPY>
{
};
template <typename TValue, typename TError>
struct ExpectedPayloadTagFor<TValue, TError, /*TrivialCopy=*/true, /*TrivialMove=*/false, /*TrivialDtor=*/true>
: integral_constant<ExpectedPayloadTag, ExpectedPayloadTag::eCALL_MOVE>
{
};
template <typename TValue, typename TError>
struct ExpectedPayloadTagFor<TValue, TError, /*TrivialCopy=*/false, /*TrivialMove=*/false, /*TrivialDtor=*/true>
: integral_constant<ExpectedPayloadTag, ExpectedPayloadTag::eCALL_BOTH>
{
};
template <typename TValue, typename TError, bool TrivialCopy, bool TrivialMove>
struct ExpectedPayloadTagFor<TValue, TError, TrivialCopy, TrivialMove, /*TrivialDtor=*/false>
: integral_constant<ExpectedPayloadTag, ExpectedPayloadTag::eCALL_DTOR>
{
};
template <typename TValue, typename TError, ExpectedPayloadTag Optimization = ExpectedPayloadTagFor<TValue, TError>::value>
class ExpectedPayload;
template <typename TValue, typename TError>
class ExpectedPayload<TValue, TError, ExpectedPayloadTag::eTRIVIAL> : public ExpectedImplBase<TValue, TError>
{
public:
using ExpectedImplBase<TValue, TError>::ExpectedImplBase;
ExpectedPayload(ExpectedPayload const&) = default;
ExpectedPayload(ExpectedPayload&&) = default;
ExpectedPayload& operator=(ExpectedPayload const&) = default;
ExpectedPayload& operator=(ExpectedPayload&&) = default;
~ExpectedPayload() = default;
};
template <typename TValue, typename TError>
class ExpectedPayload<TValue, TError, ExpectedPayloadTag::eCALL_COPY> : public ExpectedImplBase<TValue, TError>
{
public:
using ExpectedImplBase<TValue, TError>::ExpectedImplBase;
ExpectedPayload(ExpectedPayload const&) = default;
ExpectedPayload(ExpectedPayload&&) = default;
// clang-format off
ExpectedPayload& operator=(ExpectedPayload const& src)
noexcept(conjunction<disjunction<is_void<TValue>, conjunction<is_nothrow_copy_constructible<TValue>, is_nothrow_copy_assignable<TValue>>>,
disjunction<is_void<TError>, conjunction<is_nothrow_copy_constructible<TError>, is_nothrow_copy_assignable<TError>>>
>::value
)
{
this->copy_from(src);
return *this;
}
// clang-format on
ExpectedPayload& operator=(ExpectedPayload&&) = default;
~ExpectedPayload() = default;
};
template <typename TValue, typename TError>
class ExpectedPayload<TValue, TError, ExpectedPayloadTag::eCALL_MOVE> : public ExpectedImplBase<TValue, TError>
{
public:
using ExpectedImplBase<TValue, TError>::ExpectedImplBase;
ExpectedPayload(ExpectedPayload const&) = default;
ExpectedPayload(ExpectedPayload&&) = default;
ExpectedPayload& operator=(ExpectedPayload const&) = default;
// clang-format off
ExpectedPayload& operator=(ExpectedPayload&& src)
noexcept(conjunction<disjunction<is_void<TValue>, conjunction<is_nothrow_move_constructible<TValue>, is_nothrow_move_assignable<TValue>>>,
disjunction<is_void<TError>, conjunction<is_nothrow_move_constructible<TError>, is_nothrow_move_assignable<TError>>>
>::value
)
{
this->move_from(std::move(src));
return *this;
}
// clang-format on
~ExpectedPayload() = default;
};
template <typename TValue, typename TError>
class ExpectedPayload<TValue, TError, ExpectedPayloadTag::eCALL_BOTH> : public ExpectedImplBase<TValue, TError>
{
public:
using ExpectedImplBase<TValue, TError>::ExpectedImplBase;
ExpectedPayload(ExpectedPayload const&) = default;
ExpectedPayload(ExpectedPayload&&) = default;
// clang-format off
ExpectedPayload& operator=(ExpectedPayload const& src)
noexcept(conjunction<disjunction<is_void<TValue>, conjunction<is_nothrow_copy_constructible<TValue>, is_nothrow_copy_assignable<TValue>>>,
disjunction<is_void<TError>, conjunction<is_nothrow_copy_constructible<TError>, is_nothrow_copy_assignable<TError>>>
>::value
)
{
this->copy_from(src);
return *this;
}
ExpectedPayload& operator=(ExpectedPayload&& src)
noexcept(conjunction<disjunction<is_void<TValue>, conjunction<is_nothrow_move_constructible<TValue>, is_nothrow_move_assignable<TValue>>>,
disjunction<is_void<TError>, conjunction<is_nothrow_move_constructible<TError>, is_nothrow_move_assignable<TError>>>
>::value
)
{
this->move_from(std::move(src));
return *this;
}
// clang-format on
~ExpectedPayload() = default;
};
template <typename TValue, typename TError>
class ExpectedPayload<TValue, TError, ExpectedPayloadTag::eCALL_DTOR> : public ExpectedImplBase<TValue, TError>
{
public:
using ExpectedImplBase<TValue, TError>::ExpectedImplBase;
ExpectedPayload(ExpectedPayload const&) = default;
ExpectedPayload(ExpectedPayload&&) = default;
// clang-format off
ExpectedPayload& operator=(ExpectedPayload const& src)
noexcept(conjunction<disjunction<is_void<TValue>, conjunction<is_nothrow_copy_constructible<TValue>, is_nothrow_copy_assignable<TValue>>>,
disjunction<is_void<TError>, conjunction<is_nothrow_copy_constructible<TError>, is_nothrow_copy_assignable<TError>>>
>::value
)
{
this->copy_from(src);
return *this;
}
ExpectedPayload& operator=(ExpectedPayload&& src)
noexcept(conjunction<disjunction<is_void<TValue>, conjunction<is_nothrow_move_constructible<TValue>, is_nothrow_move_assignable<TValue>>>,
disjunction<is_void<TError>, conjunction<is_nothrow_move_constructible<TError>, is_nothrow_move_assignable<TError>>>
>::value
)
{
this->move_from(std::move(src));
return *this;
}
~ExpectedPayload()
noexcept(conjunction<disjunction<is_void<TValue>, is_nothrow_destructible<TValue>>,
disjunction<is_void<TError>, is_nothrow_destructible<TError>>
>::value
)
{
this->unsafe_destroy();
}
// clang-format on
};
// API //
// These classes layer the API based on the type of TValue and TError (specifically: Are they void or an object?). //
// Definitions in this section shall not affect the ABI of an `omni::expected` type; special members are all defined //
// by `= default` or by `using BaseType::BaseType`. //
// clang-format off
template <typename TValue,
typename TError,
bool CopyCtor = conjunction<disjunction<is_void<TValue>, is_copy_constructible<TValue>>,
disjunction<is_void<TError>, is_copy_constructible<TError>>
>::value,
bool DefaultCopyCtor = conjunction<bool_constant<CopyCtor>,
disjunction<is_void<TValue>, is_trivially_copy_constructible<TValue>>,
disjunction<is_void<TError>, is_trivially_copy_constructible<TError>>
>::value,
bool MoveCtor = conjunction<disjunction<is_void<TValue>, is_move_constructible<TValue>>,
disjunction<is_void<TError>, is_move_constructible<TError>>
>::value,
bool DefaultMoveCtor = conjunction<bool_constant<MoveCtor>,
disjunction<is_void<TValue>, is_trivially_move_constructible<TValue>>,
disjunction<is_void<TError>, is_trivially_move_constructible<TError>>
>::value
>
class ExpectedApiCtors;
// clang-format on
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/true, /*DefaultCopyCtor=*/true, /*MoveCtor=*/true, /*DefaultMoveCtor=*/true>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
ExpectedApiCtors(ExpectedApiCtors const&) = default;
ExpectedApiCtors(ExpectedApiCtors&&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/true, /*DefaultCopyCtor=*/true, /*MoveCtor=*/false, /*DefaultMoveCtor=*/false>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
ExpectedApiCtors(ExpectedApiCtors const&) = default;
ExpectedApiCtors(ExpectedApiCtors&&) = delete;
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/true, /*DefaultCopyCtor=*/true, /*MoveCtor=*/true, /*DefaultMoveCtor=*/false>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
ExpectedApiCtors(ExpectedApiCtors const&) = default;
constexpr ExpectedApiCtors(ExpectedApiCtors&& src) noexcept(
conjunction<disjunction<is_void<TValue>, is_nothrow_move_constructible<TValue>>,
disjunction<is_void<TError>, is_nothrow_move_constructible<TError>>>::value)
: ExpectedPayload<TValue, TError>{ ExpectedCtorBypass{}, std::move(src) }
{
}
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/false, /*DefaultCopyCtor=*/false, /*MoveCtor=*/true, /*DefaultMoveCtor=*/true>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
ExpectedApiCtors(ExpectedApiCtors const&) = delete;
ExpectedApiCtors(ExpectedApiCtors&&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/false, /*DefaultCopyCtor=*/false, /*MoveCtor=*/false, /*DefaultMoveCtor=*/false>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
ExpectedApiCtors(ExpectedApiCtors const&) = delete;
ExpectedApiCtors(ExpectedApiCtors&&) = delete;
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/false, /*DefaultCopyCtor=*/false, /*MoveCtor=*/true, /*DefaultMoveCtor=*/false>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
ExpectedApiCtors(ExpectedApiCtors const&) = delete;
constexpr ExpectedApiCtors(ExpectedApiCtors&& src) noexcept(
conjunction<disjunction<is_void<TValue>, is_nothrow_move_constructible<TValue>>,
disjunction<is_void<TError>, is_nothrow_move_constructible<TError>>>::value)
: ExpectedPayload<TValue, TError>{ ExpectedCtorBypass{}, std::move(src) }
{
}
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/true, /*DefaultCopyCtor=*/false, /*MoveCtor=*/true, /*DefaultMoveCtor=*/true>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
constexpr ExpectedApiCtors(ExpectedApiCtors const& src) noexcept(
conjunction<disjunction<is_void<TValue>, is_nothrow_copy_constructible<TValue>>,
disjunction<is_void<TError>, is_nothrow_copy_constructible<TError>>>::value)
: ExpectedPayload<TValue, TError>{ ExpectedCtorBypass{}, src }
{
}
ExpectedApiCtors(ExpectedApiCtors&&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/true, /*DefaultCopyCtor=*/false, /*MoveCtor=*/false, /*DefaultMoveCtor=*/false>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
constexpr ExpectedApiCtors(ExpectedApiCtors const& src) noexcept(
conjunction<disjunction<is_void<TValue>, is_nothrow_copy_constructible<TValue>>,
disjunction<is_void<TError>, is_nothrow_copy_constructible<TError>>>::value)
: ExpectedPayload<TValue, TError>{ ExpectedCtorBypass{}, src }
{
}
ExpectedApiCtors(ExpectedApiCtors&&) = delete;
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError>
class ExpectedApiCtors<TValue, TError, /*CopyCtor=*/true, /*DefaultCopyCtor=*/false, /*MoveCtor=*/true, /*DefaultMoveCtor=*/false>
: public ExpectedPayload<TValue, TError>
{
public:
using ExpectedPayload<TValue, TError>::ExpectedPayload;
constexpr ExpectedApiCtors(ExpectedApiCtors const& src) noexcept(
conjunction<disjunction<is_void<TValue>, is_nothrow_copy_constructible<TValue>>,
disjunction<is_void<TError>, is_nothrow_copy_constructible<TError>>>::value)
: ExpectedPayload<TValue, TError>{ ExpectedCtorBypass{}, src }
{
}
constexpr ExpectedApiCtors(ExpectedApiCtors&& src) noexcept(
conjunction<disjunction<is_void<TValue>, is_nothrow_move_constructible<TValue>>,
disjunction<is_void<TError>, is_nothrow_move_constructible<TError>>>::value)
: ExpectedPayload<TValue, TError>{ ExpectedCtorBypass{}, std::move(src) }
{
}
ExpectedApiCtors& operator=(ExpectedApiCtors const&) = default;
ExpectedApiCtors& operator=(ExpectedApiCtors&&) = default;
~ExpectedApiCtors() = default;
};
template <typename TValue, typename TError, bool IsObject = !is_void<TError>::value>
class ExpectedApiError : public ExpectedApiCtors<TValue, TError>
{
using BaseType = ExpectedApiCtors<TValue, TError>;
public:
using BaseType::BaseType;
template <typename... TArgs, typename = enable_if_t<is_constructible<TError, TArgs...>::value>>
explicit ExpectedApiError(unexpect_t, TArgs&&... args) noexcept(is_nothrow_constructible<TError, TArgs...>::value)
: BaseType{ unexpect, std::forward<TArgs>(args)... }
{
}
constexpr TError const& error() const& noexcept
{
if (CARB_UNLIKELY(this->m_state != ExpectedState::eUNEXPECTED))
invalid_expected_access_error();
return this->m_storage.error;
}
constexpr TError& error() & noexcept
{
if (CARB_UNLIKELY(this->m_state != ExpectedState::eUNEXPECTED))
invalid_expected_access_error();
return this->m_storage.error;
}
constexpr TError const&& error() const&& noexcept
{
if (CARB_UNLIKELY(this->m_state != ExpectedState::eUNEXPECTED))
invalid_expected_access_error();
return std::move(this->m_storage.error);
}
constexpr TError&& error() && noexcept
{
if (CARB_UNLIKELY(this->m_state != ExpectedState::eUNEXPECTED))
invalid_expected_access_error();
return std::move(this->m_storage.error);
}
protected:
[[noreturn]] void invalid_value_access() const&
{
CARB_FATAL_UNLESS(
this->m_state == ExpectedState::eUNEXPECTED, "internal error: omni::expected::m_state must be eUNEXPECTED");
invalid_expected_access(this->m_storage.error);
}
[[noreturn]] void invalid_value_access() &
{
CARB_FATAL_UNLESS(
this->m_state == ExpectedState::eUNEXPECTED, "internal error: omni::expected::m_state must be eUNEXPECTED");
invalid_expected_access(this->m_storage.error);
}
[[noreturn]] void invalid_value_access() &&
{
CARB_FATAL_UNLESS(
this->m_state == ExpectedState::eUNEXPECTED, "internal error: omni::expected::m_state must be eUNEXPECTED");
invalid_expected_access(std::move(this->m_storage.error));
}
[[noreturn]] void invalid_value_access() const&&
{
CARB_FATAL_UNLESS(
this->m_state == ExpectedState::eUNEXPECTED, "internal error: omni::expected::m_state must be eUNEXPECTED");
invalid_expected_access(std::move(this->m_storage.error));
}
};
template <typename TValue, typename TError>
class ExpectedApiError<TValue, TError, /*IsObject=*/false> : public ExpectedApiCtors<TValue, TError>
{
using BaseType = ExpectedApiCtors<TValue, TError>;
using BaseType::BaseType;
public:
constexpr ExpectedApiError(unexpect_t) noexcept : BaseType{ unexpect }
{
}
// NOTE: This from-unexpected constructor does not have a mirror in the `ExpectedApiError<..., IsObject=True>`
// implementation, as that conversion constructor comes from the `expected` class.
constexpr ExpectedApiError(unexpected<void> const&) noexcept : BaseType{ unexpect }
{
}
constexpr void error() const& noexcept
{
if (CARB_UNLIKELY(this->m_state != ExpectedState::eUNEXPECTED))
invalid_expected_access_error();
}
constexpr void error() && noexcept
{
if (CARB_UNLIKELY(this->m_state != ExpectedState::eUNEXPECTED))
invalid_expected_access_error();
}
protected:
[[noreturn]] void invalid_value_access() const
{
CARB_FATAL_UNLESS(
this->m_state == ExpectedState::eUNEXPECTED, "internal error: omni::expected::m_state must be eUNEXPECTED");
invalid_expected_access(ExpectedStorageVoid{});
}
};
template <typename TValue, typename TError, bool IsObject = !is_void<TValue>::value>
class ExpectedApiValue : public ExpectedApiError<TValue, TError>
{
using BaseType = ExpectedApiError<TValue, TError>;
using BaseType::BaseType;
public:
template <typename = std::enable_if_t<std::is_default_constructible<TValue>::value>>
constexpr ExpectedApiValue() noexcept(std::is_nothrow_default_constructible<TValue>::value) : BaseType{ in_place }
{
}
template <typename... TArgs, typename = enable_if_t<is_constructible<TValue, TArgs...>::value>>
explicit ExpectedApiValue(in_place_t, TArgs&&... args) noexcept(is_nothrow_constructible<TValue, TArgs...>::value)
: BaseType{ in_place, std::forward<TArgs>(args)... }
{
}
constexpr TValue& value() &
{
if (this->m_state != ExpectedState::eSUCCESS)
this->invalid_value_access();
else
return this->m_storage.value;
}
constexpr TValue const& value() const&
{
if (this->m_state != ExpectedState::eSUCCESS)
this->invalid_value_access();
else
return this->m_storage.value;
}
constexpr TValue&& value() &&
{
if (this->m_state != ExpectedState::eSUCCESS)
std::move(*this).invalid_value_access();
else
return std::move(this->m_storage.value);
}
constexpr TValue const&& value() const&&
{
if (this->m_state != ExpectedState::eSUCCESS)
std::move(*this).invalid_value_access();
else
return std::move(this->m_storage.value);
}
template <typename UValue>
constexpr TValue value_or(UValue&& default_value) const&
{
if (this->m_state == ExpectedState::eSUCCESS)
return this->m_storage.value;
else
return static_cast<TValue>(std::forward<UValue>(default_value));
}
template <typename UValue>
constexpr TValue value_or(UValue&& default_value) &&
{
if (this->m_state == ExpectedState::eSUCCESS)
return std::move(this->m_storage.value);
else
return static_cast<TValue>(std::forward<UValue>(default_value));
}
constexpr TValue const& operator*() const& noexcept
{
return this->value();
}
constexpr TValue& operator*() & noexcept
{
return this->value();
}
constexpr TValue const&& operator*() const&& noexcept
{
return std::move(*this).value();
}
constexpr TValue&& operator*() && noexcept
{
return std::move(*this).value();
}
constexpr TValue const* operator->() const noexcept
{
return &this->value();
}
constexpr TValue* operator->() noexcept
{
return &this->value();
}
template <typename... TArgs>
constexpr std::enable_if_t<std::is_nothrow_constructible<TValue, TArgs...>::value, TValue&> emplace(TArgs&&... args) noexcept
{
return this->emplace_impl(std::forward<TArgs>(args)...);
}
template <typename U, typename... TArgs>
constexpr std::enable_if_t<std::is_nothrow_constructible<TValue, std::initializer_list<U>&, TArgs...>::value, TValue&> emplace(
std::initializer_list<U>& arg0, TArgs&&... args) noexcept
{
return this->emplace_impl(arg0, std::forward<TArgs>(args)...);
}
private:
template <typename... TArgs>
constexpr TValue& emplace_impl(TArgs&&... args)
{
if (this->m_state == ExpectedState::eSUCCESS)
expected_reinit(carb::cpp::addressof(this->m_storage.value), carb::cpp::addressof(this->m_storage.value),
std::forward<TArgs>(args)...);
else
expected_reinit(carb::cpp::addressof(this->m_storage.error), carb::cpp::addressof(this->m_storage.value),
std::forward<TArgs>(args)...);
this->m_state = ExpectedState::eSUCCESS;
return this->m_storage.value;
}
};
template <typename TValue, typename TError>
class ExpectedApiValue<TValue, TError, /*IsObject=*/false> : public ExpectedApiError<TValue, TError>
{
using BaseType = ExpectedApiError<TValue, TError>;
public:
using BaseType::BaseType;
constexpr explicit ExpectedApiValue() noexcept : BaseType{ in_place }
{
}
constexpr explicit ExpectedApiValue(in_place_t) noexcept : BaseType{ in_place }
{
}
ExpectedApiValue(ExpectedApiValue const&) = default;
ExpectedApiValue(ExpectedApiValue&&) = default;
ExpectedApiValue& operator=(ExpectedApiValue const&) = default;
ExpectedApiValue& operator=(ExpectedApiValue&&) = default;
~ExpectedApiValue() = default;
constexpr void value() &
{
if (this->m_state != ExpectedState::eSUCCESS)
this->invalid_value_access();
}
constexpr void value() const&
{
if (this->m_state != ExpectedState::eSUCCESS)
this->invalid_value_access();
}
constexpr void value() &&
{
if (this->m_state != ExpectedState::eSUCCESS)
std::move(*this).invalid_value_access();
}
constexpr void value() const&&
{
if (this->m_state != ExpectedState::eSUCCESS)
std::move(*this).invalid_value_access();
}
constexpr void operator*() const noexcept
{
value();
}
constexpr void emplace() noexcept
{
if (this->m_state != ExpectedState::eSUCCESS)
{
// Destroy the error state if one exists (note that if TError is also void, there is no m_storage)
this->unsafe_destroy();
this->m_state = ExpectedState::eSUCCESS;
}
}
};
template <typename TValue, typename TError>
class ExpectedImpl : public ExpectedApiValue<TValue, TError>
{
using BaseType = ExpectedApiValue<TValue, TError>;
static_assert(!std::is_same<in_place_t, std::remove_cv_t<TValue>>::value, "expected value can not be in_place_t");
static_assert(!std::is_same<in_place_t, std::remove_cv_t<TError>>::value, "expected error can not be in_place_t");
static_assert(!std::is_same<unexpect_t, std::remove_cv_t<TValue>>::value, "expected value can not be unexpect_t");
static_assert(!std::is_same<unexpect_t, std::remove_cv_t<TError>>::value, "expected error can not be unexpect_t");
public:
using BaseType::BaseType;
};
// Monadic Operations //
// Functions like `expected::transform` are complicated by `void`, which is syntactically separate from a value type. //
// The implementation is split into separate parts: `ExpectedMonadOpImpl`, which contains the implementation of all //
// valid monadic operations and is specialized per type transform; `ExpectedOp___` per function, which chooses the //
// proper type transformation. The grouping of functions this way seems slightly easier to read, but it still hairy. //
// MSC thinks some of the code branches in these implementations are unreachable. Specifically, it dislikes the use of a
// return statement after a call to `carb::cpp::invoke(f)` (with no second parameter). The invoke implementation is
// not marked `[[noreturn]]` or anything like that and the unit tests exercise that code path, so it is unclear why MSC
// thinks that. This can be removed when MS fixes the bug.
CARB_IGNOREWARNING_MSC_WITH_PUSH(4702) // unreachable code
template <typename TValue, typename TError, typename RValue, typename RError>
struct ExpectedMonadOpImpl
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, TError> transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, TError>(
in_place, carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value()));
else
return omni::expected<RValue, TError>(unexpect, std::forward<TExpected>(ex).error());
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, RError> and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value());
else
return omni::expected<RValue, RError>(unexpect, std::forward<TExpected>(ex).error());
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, RError> transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, RError>(in_place, std::forward<TExpected>(ex).value());
else
return omni::expected<RValue, RError>(
unexpect, carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error()));
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, RError> or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, RError>(in_place, std::forward<TExpected>(ex).value());
else
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error());
}
};
template <typename TError, typename RError>
struct ExpectedMonadOpImpl<void, TError, void, RError>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, TError> transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
carb::cpp::invoke(std::forward<FTransform>(f));
return omni::expected<void, TError>(in_place);
}
else
{
return omni::expected<void, TError>(unexpect, std::forward<TExpected>(ex).error());
}
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, RError> and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f));
else
return omni::expected<void, RError>(unexpect, std::forward<TExpected>(ex).error());
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, RError> transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<void, RError>(in_place);
else
return omni::expected<void, RError>(
unexpect, carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error()));
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, RError> or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<void, RError>(in_place);
else
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error());
}
};
template <typename TValue, typename RValue>
struct ExpectedMonadOpImpl<TValue, void, RValue, void>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, void>(
in_place, carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value()));
else
return omni::expected<RValue, void>(unexpect);
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value());
else
return omni::expected<RValue, void>(unexpect);
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
return omni::expected<RValue, void>(in_place, std::forward<TExpected>(ex).value());
}
else
{
carb::cpp::invoke(std::forward<FTransform>(f));
return omni::expected<RValue, void>(unexpect);
}
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, void>(in_place, std::forward<TExpected>(ex).value());
else
return carb::cpp::invoke(std::forward<FTransform>(f));
}
};
template <typename TValue, typename TError, typename RError>
struct ExpectedMonadOpImpl<TValue, TError, void, RError>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, TError> transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value());
return omni::expected<void, TError>(in_place);
}
else
{
return omni::expected<void, TError>(unexpect, std::forward<TExpected>(ex).error());
}
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, RError> and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value());
else
return omni::expected<void, RError>(unexpect, std::forward<TExpected>(ex).error());
}
};
template <typename TError, typename RValue, typename RError>
struct ExpectedMonadOpImpl<void, TError, RValue, RError>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, TError> transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, TError>(in_place, carb::cpp::invoke(std::forward<FTransform>(f)));
else
return omni::expected<RValue, TError>(unexpect, std::forward<TExpected>(ex).error());
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, RError> and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f));
else
return omni::expected<RValue, RError>(unexpect, std::forward<TExpected>(ex).error());
}
};
template <typename TValue>
struct ExpectedMonadOpImpl<TValue, void, void, void>
{
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value());
return TReturn(in_place);
}
else
{
return TReturn(unexpect);
}
}
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).value());
else
return TReturn(unexpect);
}
};
template <typename RValue>
struct ExpectedMonadOpImpl<void, void, RValue, void>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, void>(in_place, carb::cpp::invoke(std::forward<FTransform>(f)));
else
return omni::expected<RValue, void>(unexpect);
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f));
else
return omni::expected<RValue, void>(unexpect);
}
};
template <typename TValue, typename TError, typename RValue>
struct ExpectedMonadOpImpl<TValue, TError, RValue, void>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
return omni::expected<RValue, void>(in_place, std::forward<TExpected>(ex).value());
}
else
{
carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error());
return omni::expected<RValue, void>(unexpect);
}
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, void> or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, void>(in_place, std::forward<TExpected>(ex).value());
else
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error());
}
};
template <typename TError>
struct ExpectedMonadOpImpl<void, TError, void, void>
{
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
return TReturn(in_place);
}
else
{
carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error());
return TReturn(unexpect);
}
}
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return TReturn(in_place);
else
return carb::cpp::invoke(std::forward<FTransform>(f), std::forward<TExpected>(ex).error());
}
};
template <typename TValue, typename RValue, typename RError>
struct ExpectedMonadOpImpl<TValue, void, RValue, RError>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, RError> transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, RError>(in_place, std::forward<TExpected>(ex).value());
else
return omni::expected<RValue, RError>(unexpect, carb::cpp::invoke(std::forward<FTransform>(f)));
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<RValue, RError> or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<RValue, RError>(in_place, std::forward<TExpected>(ex).value());
else
return carb::cpp::invoke(std::forward<FTransform>(f));
}
};
template <typename RError>
struct ExpectedMonadOpImpl<void, void, void, RError>
{
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, RError> transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<void, RError>(in_place);
else
return omni::expected<void, RError>(unexpect, carb::cpp::invoke(std::forward<FTransform>(f)));
}
template <typename FTransform, typename TExpected>
static constexpr omni::expected<void, RError> or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return omni::expected<void, RError>(in_place);
else
return carb::cpp::invoke(std::forward<FTransform>(f));
}
};
template <>
struct ExpectedMonadOpImpl<void, void, void, void>
{
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn transform(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
carb::cpp::invoke(std::forward<FTransform>(f));
return TReturn(in_place);
}
else
{
return TReturn(unexpect);
}
}
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn and_then(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return carb::cpp::invoke(std::forward<FTransform>(f));
else
return TReturn(unexpect);
}
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn transform_error(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
{
return TReturn(in_place);
}
else
{
carb::cpp::invoke(std::forward<FTransform>(f));
return TReturn(unexpect);
}
}
template <typename FTransform, typename TExpected, typename TReturn = omni::expected<void, void>>
static constexpr TReturn or_else(FTransform&& f, TExpected&& ex)
{
if (ex.has_value())
return TReturn(in_place);
else
return carb::cpp::invoke(std::forward<FTransform>(f));
}
};
template <typename FTransform, typename TExpected, typename RefExpected, typename = void_t<>>
struct ExpectedOpTransform
{
};
template <typename FTransform, typename TValue, typename TError, typename RefExpected>
struct ExpectedOpTransform<FTransform,
omni::expected<TValue, TError>,
RefExpected,
void_t<carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().value())>>>
: ExpectedMonadOpImpl<TValue, TError, carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().value())>, TError>
{
};
template <typename FTransform, typename TError, typename RefExpected>
struct ExpectedOpTransform<FTransform, omni::expected<void, TError>, RefExpected, void_t<carb::cpp::invoke_result_t<FTransform>>>
: ExpectedMonadOpImpl<void, TError, carb::cpp::invoke_result_t<FTransform>, TError>
{
};
template <typename FTransform, typename TExpected, typename RefExpected, typename = void_t<>>
struct ExpectedOpAndThen
{
template <typename UExpected>
static constexpr void and_then(FTransform&&, UExpected&&)
{
// HELLO! If you're seeing one of these failures, that means you called `.and_then` with a callable that can't
// be called with the `value_type` of the expected. This base template is only used through misuse of that
// member function, so one of these `static_assert`s will always hit and give a slightly better diagnostic.
static_assert(is_void<typename TExpected::value_type>::value,
"function provided to `and_then` must accept the `expected::value_type`");
static_assert(!is_void<typename TExpected::value_type>::value,
"function provided to `and_then` must accept no parameters");
}
};
template <typename TExpected, typename UExpected>
struct ExpectedOpAndThenImpl
{
template <typename FTransform, typename VExpected>
static constexpr void and_then(FTransform&&, VExpected&&)
{
// HELLO! If you're seeing this failure, it means the function provided to `.and_then` did not return an
// `expected` when called with the `value_type`.
static_assert(is_void<FTransform>::value, "function provided to `and_then` must return an `expected<T, E>`");
}
};
template <typename TValue, typename TError, typename UValue, typename UError>
struct ExpectedOpAndThenImpl<omni::expected<TValue, TError>, omni::expected<UValue, UError>>
: ExpectedMonadOpImpl<TValue, TError, UValue, UError>
{
};
template <typename FTransform, typename TValue, typename TError, typename RefExpected>
struct ExpectedOpAndThen<FTransform,
omni::expected<TValue, TError>,
RefExpected,
void_t<carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().value())>>>
: ExpectedOpAndThenImpl<omni::expected<TValue, TError>,
carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().value())>>
{
};
template <typename FTransform, typename TError, typename RefExpected>
struct ExpectedOpAndThen<FTransform, omni::expected<void, TError>, RefExpected, void_t<carb::cpp::invoke_result_t<FTransform>>>
: ExpectedOpAndThenImpl<omni::expected<void, TError>, carb::cpp::invoke_result_t<FTransform>>
{
};
template <typename FTransform, typename TExpected, typename RefExpected, typename = void_t<>>
struct ExpectedOpTransformError
{
};
template <typename FTransform, typename TValue, typename TError, typename RefExpected>
struct ExpectedOpTransformError<FTransform,
omni::expected<TValue, TError>,
RefExpected,
void_t<carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().error())>>>
: ExpectedMonadOpImpl<TValue,
TError,
TValue,
carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().error())>>
{
};
template <typename FTransform, typename TValue, typename RefExpected>
struct ExpectedOpTransformError<FTransform, omni::expected<TValue, void>, RefExpected, void_t<carb::cpp::invoke_result_t<FTransform>>>
: ExpectedMonadOpImpl<TValue, void, TValue, carb::cpp::invoke_result_t<FTransform>>
{
};
template <typename FTransform, typename TExpected, typename RefExpected, typename = void_t<>>
struct ExpectedOpOrElse
{
template <typename UExpected>
static constexpr void or_else(FTransform&&, UExpected&&)
{
// HELLO! If you're seeing one of these failures, that means you called `.or_else` with a callable that can't
// be called with the `value_type` of the expected. This base template is only used through misuse of that
// member function, so one of these `static_assert`s will always hit and give a slightly better diagnostic.
static_assert(is_void<typename TExpected::error_type>::value,
"function provided to `or_else` must accept the `expected::value_type`");
static_assert(!is_void<typename TExpected::error_type>::value,
"function provided to `or_else` must accept no parameters");
}
};
template <typename TExpected, typename UExpected>
struct ExpectedOpOrElseImpl
{
template <typename FTransform, typename VExpected>
static constexpr void or_else(FTransform&&, VExpected&&)
{
// HELLO! If you're seeing this failure, it means the function provided to `.or_else` did not return an
// `expected` when called with the `value_type`.
static_assert(is_void<FTransform>::value, "function provided to `or_else` must return an `expected<T, E>`");
}
};
template <typename TValue, typename TError, typename UValue, typename UError>
struct ExpectedOpOrElseImpl<omni::expected<TValue, TError>, omni::expected<UValue, UError>>
: ExpectedMonadOpImpl<TValue, TError, UValue, UError>
{
};
template <typename FTransform, typename TValue, typename TError, typename RefExpected>
struct ExpectedOpOrElse<FTransform,
omni::expected<TValue, TError>,
RefExpected,
void_t<carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().error())>>>
: ExpectedOpOrElseImpl<omni::expected<TValue, TError>,
carb::cpp::invoke_result_t<FTransform, decltype(std::declval<RefExpected>().error())>>
{
};
template <typename FTransform, typename TValue, typename RefExpected>
struct ExpectedOpOrElse<FTransform, omni::expected<TValue, void>, RefExpected, void_t<carb::cpp::invoke_result_t<FTransform>>>
: ExpectedOpOrElseImpl<omni::expected<TValue, void>, carb::cpp::invoke_result_t<FTransform>>
{
};
CARB_IGNOREWARNING_MSC_POP
// Comparison Functions //
template <typename TExpected1, typename TExpected2, typename = void_t<>>
struct ExpectedComparer
{
};
template <typename TValue1, typename TError1, typename TValue2, typename TError2>
struct ExpectedComparer<
expected<TValue1, TError1>,
expected<TValue2, TError2>,
std::enable_if_t<negation<disjunction<is_void<TValue1>, is_void<TError1>, is_void<TValue2>, is_void<TError2>>>::value>>
{
template <typename TExpectedLhs, typename TExpectedRhs>
static constexpr bool eq(TExpectedLhs const& lhs, TExpectedRhs const& rhs)
{
if (lhs.has_value() && rhs.has_value())
return lhs.value() == rhs.value();
else if (!lhs.has_value() && !rhs.has_value())
return lhs.error() == rhs.error();
else
return false;
}
};
template <typename TValue1, typename TValue2>
struct ExpectedComparer<expected<TValue1, void>,
expected<TValue2, void>,
enable_if_t<negation<disjunction<is_void<TValue1>, is_void<TValue2>>>::value>>
{
template <typename TExpectedLhs, typename TExpectedRhs>
static constexpr bool eq(TExpectedLhs const& lhs, TExpectedRhs const& rhs)
{
if (lhs.has_value() && rhs.has_value())
return lhs.value() == rhs.value();
else if (!lhs.has_value() && !rhs.has_value())
return true;
else
return false;
}
};
template <typename TError1, typename TError2>
struct ExpectedComparer<expected<void, TError1>,
expected<void, TError2>,
enable_if_t<negation<disjunction<is_void<TError1>, is_void<TError2>>>::value>>
{
template <typename TExpectedLhs, typename TExpectedRhs>
static constexpr bool eq(TExpectedLhs const& lhs, TExpectedRhs const& rhs)
{
if (lhs.has_value() && rhs.has_value())
return true;
else if (!lhs.has_value() && !rhs.has_value())
return lhs.error() == rhs.error();
else
return false;
}
};
template <>
struct ExpectedComparer<expected<void, void>, expected<void, void>, void>
{
template <typename TExpected>
static constexpr bool eq(TExpected const& lhs, TExpected const& rhs)
{
return lhs.has_value() == rhs.has_value();
}
};
template <typename TErrorLhs, typename TErrorRhs>
struct ExpectedUnexpectedComparer
{
template <typename TExpected, typename TUnexpected>
static constexpr bool eq(TExpected const& lhs, TUnexpected const& rhs)
{
return !lhs.has_value() && static_cast<bool>(lhs.error() == rhs.error());
}
};
template <>
struct ExpectedUnexpectedComparer<void, void>
{
template <typename TExpected>
static constexpr bool eq(TExpected const& lhs, unexpected<void> const&)
{
return !lhs.has_value();
}
};
} // namespace detail
} // namespace omni