Created
January 8, 2025 05:47
-
-
Save BreadFish64/453bc4005b2e28578738ed83851d7720 to your computer and use it in GitHub Desktop.
Revisions
-
BreadFish64 revised this gist
Jan 8, 2025 . No changes.There are no files selected for viewing
-
BreadFish64 created this gist
Jan 8, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,1672 @@ #pragma once #include <cassert> #include <concepts> #include <memory> #include <type_traits> #include <utility> namespace breaded { #pragma region Forward Declarations template <class E> class bad_expected_access; template <class E> class unexpected; template <class T, class E> class expected; #pragma endregion // https://en.cppreference.com/w/cpp/utility/expected/unexpect_t struct unexpect_t { constexpr explicit unexpect_t() = default; }; // https://en.cppreference.com/w/cpp/utility/expected/unexpect_t constexpr unexpect_t unexpect{}; #pragma region Traits namespace details { struct in_place_from_return_t { constexpr explicit in_place_from_return_t() = default; }; constexpr in_place_from_return_t in_place_from_return{}; struct unexpect_from_return_t { constexpr explicit unexpect_from_return_t() = default; }; constexpr unexpect_from_return_t unexpect_from_return{}; template <class T> struct IsSpecializationOfUnexpected : std::false_type {}; template <class T, class E> struct IsSpecializationOfUnexpected<breaded::expected<T, E>> : std::true_type {}; template <class T> constexpr bool kIsSpecializationOfUnexpected = IsSpecializationOfUnexpected<T>::value; template <class T> struct IsSpecializationOfExpected : std::false_type {}; template <class T, class E> struct IsSpecializationOfExpected<breaded::expected<T, E>> : std::true_type {}; template <class T> constexpr bool kIsSpecializationOfExpected = IsSpecializationOfExpected<T>::value; // https://en.cppreference.com/w/cpp/named_req/Destructible // This isn't quite the same as std::is_destructible_v<T> // This does not allow arrays or reference types. template <class T> concept NamedRequirementDestructible = std::is_nothrow_destructible_v<T> && !std::is_array_v<T> && !std::is_reference_v<T>; template <typename T> concept NotTag = !std::is_same_v<std::remove_cvref_t<T>, std::in_place_t> && !std::is_same_v<std::remove_cvref_t<T>, breaded::unexpect_t>; template <typename E> concept ValidExpectedErrorType = std::is_object_v<E> && !std::is_const_v<E> && !std::is_volatile_v<E> && !kIsSpecializationOfExpected<E>; template <class T> concept ValidExpectedValueType = NotTag<T> && !std::is_reference_v<T> && !std::is_function_v<T> && !kIsSpecializationOfUnexpected<T> && (std::is_void_v<T> || NamedRequirementDestructible<T>); } // namespace details #pragma endregion #pragma region bad_expected_access // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access template <> class bad_expected_access<void> : public std::exception { public: // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::what const char* what() const noexcept override { return "Attempted to access the value of an errored expected type"; } protected: // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::bad_expected_access explicit bad_expected_access() = default; bad_expected_access(const bad_expected_access& other) = default; bad_expected_access(bad_expected_access&& other) = default; ~bad_expected_access() override = default; bad_expected_access& operator=(const bad_expected_access& other) = default; bad_expected_access& operator=(bad_expected_access&& other) = default; }; // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access template <class E> class bad_expected_access : public breaded::bad_expected_access<void> { public: // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::bad_expected_access explicit bad_expected_access(E e) : error_{std::move(e)} {} // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] const E& error() const& noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] E& error() & noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] const E&& error() const&& noexcept { return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] E&& error() && noexcept { return std::move(error_); } private: E error_; }; #pragma endregion #pragma region Unexpected // https://en.cppreference.com/w/cpp/utility/expected/unexpected template <class E> class unexpected { static_assert(details::ValidExpectedErrorType<E>); public: unexpected() = delete; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor constexpr unexpected(const unexpected&) noexcept(std::is_nothrow_copy_constructible_v<E>) = default; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor template <class Err = E> requires(!std::is_same_v<std::remove_cvref_t<Err>, unexpected> && !std::is_same_v<std::remove_cvref_t<Err>, std::in_place_t> && std::is_constructible_v<E, Err>) constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>) : error_(std::forward<Err>(e)) {} // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor template <class... Args> requires(std::is_constructible_v<E, Args...>) constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>) : error_(std::forward<Args>(args)...) {} // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor template <class U, class... Args> requires(std::is_constructible_v<E, std::initializer_list<U>&, Args...>) constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args) noexcept( std::is_nothrow_constructible_v<E, std::initializer_list<U>&, Args...>) : error_(il, std::forward<Args>(args)...) {} // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr const E& error() const& noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr E& error() & noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr const E&& error() const&& noexcept { return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr E&& error() && noexcept { return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#swap constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) { // Prefer custom swap friend function, and use move-based std::swap as a fallback // https://en.cppreference.com/w/cpp/language/adl using std::swap; swap(error_, other.error_); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#operator.3D.3D.28std::unexpected.29 template <class E2> [[nodiscard]] friend constexpr bool operator==(const unexpected& x, const breaded::unexpected<E2>& y) noexcept(noexcept(x.error() == y.error())) { return x.error() == y.error(); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#swap.28std::unexpected.29 friend constexpr void swap(unexpected& x, unexpected& y) noexcept(std::is_nothrow_swappable_v<E>) { x.swap(y); } private: E error_; }; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#Deduction_guides template <class E> unexpected(E) -> unexpected<E>; #pragma endregion namespace details { template <class NewType, class OldType, class... Args> constexpr bool kUnionAssignable = std::is_constructible_v<NewType, Args...> && (std::is_nothrow_constructible_v<NewType, Args...> || std::is_nothrow_move_constructible_v<NewType> || std::is_nothrow_move_constructible_v<OldType>); template <class NewType, class OldType, class... Args> constexpr bool kNothrowUnionAssignable = kUnionAssignable<NewType, OldType, Args...> && std::is_nothrow_constructible_v<NewType, Args...>; // This function allows assigning to an inactive union member, // while preserving the old active member if the assignment throws an exception. // This is required for std::expected since it can never be value-less // // "If an exception is thrown, the old value is retained; *this does not become valueless." // https://en.cppreference.com/w/cpp/utility/expected/operator%3D#Helper_function_template template <class NewType, class OldType, class... Args> constexpr void UnionAssign(NewType& newVal, OldType& oldVal, Args&&... args) noexcept(kNothrowUnionAssignable<NewType, OldType, Args...>) requires(kUnionAssignable<NewType, OldType, Args...>) { if constexpr (std::is_nothrow_constructible_v<NewType, Args...>) { std::destroy_at(std::addressof(oldVal)); std::construct_at(std::addressof(newVal), std::forward<Args>(args)...); } else if constexpr (std::is_nothrow_move_constructible_v<NewType>) { NewType temp(std::forward<Args>(args)...); std::destroy_at(std::addressof(oldVal)); std::construct_at(std::addressof(newVal), std::move(temp)); } else if constexpr (std::is_nothrow_move_constructible_v<OldType>) { OldType temp(std::move(oldVal)); std::destroy_at(std::addressof(oldVal)); try { std::construct_at(std::addressof(newVal), std::forward<Args>(args)...); } catch (...) { std::construct_at(std::addressof(oldVal), std::move(temp)); throw; } } else { static_assert(sizeof(NewType) == 0, "Sanity check failed. Cannot call without fulfilling one of these conditions"); } } template <class T, class U> constexpr bool kUnionSwappable = std::is_move_constructible_v<T> && std::is_move_constructible_v<U> && (std::is_nothrow_move_constructible_v<T> || std::is_nothrow_move_constructible_v<U>); template <class T, class U> constexpr bool kNothrowUnionSwappable = std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_constructible_v<U>; // https://en.cppreference.com/w/cpp/utility/expected/swap template <class T, class U> constexpr void UnionSwap(T& dstT, U& dstU, T&& srcT, U&& srcU) noexcept(kNothrowUnionSwappable<T, U>) requires(kUnionSwappable<T, U>) { if constexpr (std::is_nothrow_move_constructible_v<T>) { T tmp(std::move(srcT)); std::destroy_at(std::addressof(srcT)); if constexpr (std::is_nothrow_move_constructible_v<U>) { std::construct_at(std::addressof(dstU), std::move(srcU)); } else { try { std::construct_at(std::addressof(dstU), std::move(srcU)); } catch (...) { std::construct_at(std::addressof(srcT), std::move(tmp)); throw; } } std::destroy_at(std::addressof(srcU)); std::construct_at(std::addressof(dstT), std::move(tmp)); } else { static_assert(std::is_nothrow_move_constructible_v<U>, "Either T or U must be nothrow_move_constructible to enforce strong exception safety"); UnionSwap(dstU, dstT, std::move(srcU), std::move(srcT)); } } } // namespace details #pragma region Expected T // https://en.cppreference.com/w/cpp/utility/expected template <class T, class E> class expected { static_assert(details::ValidExpectedValueType<T>); static_assert(details::ValidExpectedErrorType<E>); static_assert(!std::is_void_v<T>, "Sanity check failed. This should be handled in a specialization"); template <typename Self, typename F> struct AndThenReturn { using type = std::remove_cvref_t<std::invoke_result_t<F, decltype(*std::declval<Self>())>>; }; template <typename Self, typename F> struct TransformReturn { using URaw = std::invoke_result_t<F, decltype(*std::declval<Self>())>; using U = std::remove_cvref_t<URaw>; using type = breaded::expected<U, E>; }; template <typename Self, typename F> struct OrElseReturn { using type = std::remove_cvref_t<std::invoke_result_t<F, decltype(std::declval<Self>().error())>>; }; template <typename Self, typename F> struct TransformErrorReturn { using GRaw = std::invoke_result_t<F, decltype(std::declval<Self>().error())>; using G = std::remove_cvref_t<GRaw>; using type = breaded::expected<T, G>; }; public: #pragma region Member types // https://en.cppreference.com/w/cpp/utility/expected#Member_types using value_type = T; using error_type = E; using unexpected_type = breaded::unexpected<E>; #pragma endregion #pragma region Member alias templates // https://en.cppreference.com/w/cpp/utility/expected#Member_alias_templates template <class U> using rebind = breaded::expected<U, error_type>; #pragma endregion private: template <typename U> static constexpr bool kSameAsThis = std::is_same_v<expected, std::remove_cvref_t<U>>; static constexpr bool kValueTypeIsBool = std::is_same_v<bool, std::remove_cvref_t<T>>; template <class U> static constexpr bool kExpectedConvertibleToValueType = std::is_constructible_v<T, U&> || std::is_constructible_v<T, U> || std::is_constructible_v<T, const U&> || std::is_constructible_v<T, const U> || std::is_convertible_v<U&, T> || std::is_convertible_v<U, T> || std::is_convertible_v<const U&, T> || std::is_convertible_v<const U, T>; template <class U> static constexpr bool kExpectedConvertibleToErrorType = std::is_constructible_v<unexpected_type, U&> || std::is_constructible_v<unexpected_type, U> || std::is_constructible_v<unexpected_type, const U&> || std::is_constructible_v<unexpected_type, const U>; public: #pragma region Member functions // https://en.cppreference.com/w/cpp/utility/expected#Member_functions #pragma region constructor // Default value constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected() noexcept(std::is_nothrow_default_constructible_v<T>) requires(std::is_default_constructible_v<T>) : value_(), hasValue_(true) {} // Non-trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(const expected& other) noexcept(std::is_nothrow_copy_constructible_v<T> && std::is_nothrow_copy_constructible_v<E>) requires(std::is_copy_constructible_v<T> && std::is_copy_constructible_v<E> && !(std::is_trivially_copy_constructible_v<T> && std::is_trivially_copy_constructible_v<E>)) { constructFromExpected(other); } // Trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(const expected& other) requires(std::is_copy_constructible_v<T> && std::is_copy_constructible_v<E> && (std::is_trivially_copy_constructible_v<T> && std::is_trivially_copy_constructible_v<E>)) = default; // This constructor is defined as deleted unless is_copy_constructible_v<T> is true // and is_copy_constructible_v<E> is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L7423 expected(const expected&) = delete; // Non-trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(expected&& other) noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_constructible_v<E>) requires(std::is_move_constructible_v<T> && std::is_move_constructible_v<E> && !(std::is_trivially_move_constructible_v<T> && std::is_trivially_move_constructible_v<E>)) { constructFromExpected(std::move(other)); } // Trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(expected&& other) requires(std::is_move_constructible_v<T> && std::is_move_constructible_v<E> && (std::is_trivially_move_constructible_v<T> && std::is_trivially_move_constructible_v<E>)) = default; // Converting copy construct from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U, class G> requires(!kSameAsThis<breaded::expected<U, G>> && std::is_constructible_v<T, const U&> && (kValueTypeIsBool || !kExpectedConvertibleToValueType<breaded::expected<U, G>>) && !kExpectedConvertibleToErrorType<breaded::expected<U, G>>) constexpr explicit(!std::is_convertible_v<const U&, T> || !std::is_convertible_v<const G&, E>) expected(const breaded::expected<U, G>& other) noexcept(std::is_nothrow_constructible_v<T, const U&> && std::is_nothrow_constructible_v<E, const G&>) { constructFromExpected(other); } // Converting move construct from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U, class G> requires(!kSameAsThis<breaded::expected<U, G>> && std::is_constructible_v<T, U> && (kValueTypeIsBool || !kExpectedConvertibleToValueType<breaded::expected<U, G>>) && !kExpectedConvertibleToErrorType<breaded::expected<U, G>>) constexpr explicit(!std::is_convertible_v<U, T> || !std::is_convertible_v<G, E>) expected(breaded::expected<U, G>&& other) noexcept(std::is_nothrow_constructible_v<T, U> && std::is_nothrow_constructible_v<E, G>) { constructFromExpected(std::move(other)); } // Constructor from value // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U = T> requires(details::NotTag<T> && !kSameAsThis<U> && std::is_constructible_v<T, U> && !details::kIsSpecializationOfUnexpected<std::remove_cvref_t<U>> && (!kValueTypeIsBool || !details::kIsSpecializationOfUnexpected<std::remove_cvref_t<U>>)) constexpr explicit(!std::is_convertible_v<U, T>) expected(U&& v) noexcept(std::is_nothrow_constructible_v<T, U>) : value_(std::forward<U>(v)), hasValue_(true) {} // Copy constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class G> requires(std::is_constructible_v<E, const G&>) constexpr explicit(!std::is_convertible_v<const G&, E>) expected(const breaded::unexpected<G>& e) noexcept(std::is_nothrow_constructible_v<E, const G&>) : error_(e.error()), hasValue_(false) {} // Move constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class G> requires(std::is_constructible_v<E, G>) constexpr explicit(!std::is_convertible_v<G, E>) expected(breaded::unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G>) : error_(std::move(e).error()), hasValue_(false) {} // In-place value constructor from args // https://en.cppreference.com/w/cpp/utility/expected/expected template <class... Args> requires(std::is_constructible_v<T, Args...>) constexpr explicit expected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>) : value_(std::forward<Args>(args)...), hasValue_(true) {} // In-place value constructor from initializer list // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U, class... Args> requires(std::is_constructible_v<T, std::initializer_list<U>&, Args...>) constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args) noexcept( std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>) : value_(il, std::forward<Args>(args)...), hasValue_(true) {} // In-place error constructor from args // https://en.cppreference.com/w/cpp/utility/expected/expected template <class... Args> requires(std::is_constructible_v<E, Args...>) constexpr explicit expected(breaded::unexpect_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>) : error_(std::forward<Args>(args)...), hasValue_(false) {} // In-place error constructor from initializer list // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U, class... Args> requires(std::is_constructible_v<E, std::initializer_list<U>&, Args...>) constexpr explicit expected(breaded::unexpect_t, std::initializer_list<U> il, Args&&... args) noexcept( std::is_nothrow_constructible_v<E, std::initializer_list<U>&, Args...>) : error_(il, std::forward<Args>(args)...), hasValue_(false) {} #pragma endregion // https://en.cppreference.com/w/cpp/utility/expected/~expected#Main_template_destructor constexpr ~expected() noexcept { destroy(); } // https://en.cppreference.com/w/cpp/utility/expected/~expected#Main_template_destructor ~expected() requires(std::is_trivially_destructible_v<T> && std::is_trivially_destructible_v<E>) = default; #pragma region Assignment // Non-trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(const expected& other) noexcept(std::is_nothrow_copy_assignable_v<T> && std::is_nothrow_copy_assignable_v<E> && details::kNothrowUnionAssignable<T, E, const T&> && details::kNothrowUnionAssignable<E, T, const E&>) requires(std::is_copy_assignable_v<T> && std::is_copy_assignable_v<E> && details::kUnionAssignable<T, E, const T&> && details::kUnionAssignable<E, T, const E&> && !(std::is_trivially_copy_assignable_v<T> && std::is_trivially_copy_assignable_v<E>)) { assignFromExpected(other); return *this; } // Trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(const expected& other) requires(std::is_copy_assignable_v<T> && std::is_copy_assignable_v<E> && details::kUnionAssignable<T, E, const T&> && details::kUnionAssignable<E, T, const E&> && (std::is_trivially_copy_assignable_v<T> && std::is_trivially_copy_assignable_v<E>)) = default; // This operator is defined as deleted unless is_copy_constructible_v<T> is true and is_copy_assignable_v<T> is true // and is_copy_assignable_v<E> is true and is_copy_constructible_v<E> is true and // is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L7796 expected& operator=(const expected&) = delete; // Non-trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(expected&& other) noexcept(std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_assignable_v<E> && details::kNothrowUnionAssignable<T, E, T> && details::kNothrowUnionAssignable<E, T, E>) requires(std::is_move_assignable_v<T> && std::is_move_assignable_v<E> && details::kUnionAssignable<T, E, T> && details::kUnionAssignable<E, T, E> && !(std::is_trivially_move_assignable_v<T> && std::is_trivially_move_assignable_v<E>)) { assignFromExpected(std::move(other)); return *this; } // Trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(expected&& other) requires(std::is_move_assignable_v<T> && std::is_move_assignable_v<E> && details::kUnionAssignable<T, E, T> && details::kUnionAssignable<E, T, E> && (std::is_trivially_move_assignable_v<T> && std::is_trivially_move_assignable_v<E>)) = default; // Assignment from value // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template <class U = T> constexpr expected& operator=(U&& v) noexcept(std::is_nothrow_assignable_v<T, U> && details::kNothrowUnionAssignable<T, E, U>) requires(!kSameAsThis<U> && !details::kIsSpecializationOfExpected<U> && std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> && details::kUnionAssignable<T, E, U>) { if (hasValue_) { value_ = std::forward<U>(v); } else { details::UnionAssign(value_, error_, std::forward<U>(v)); hasValue_ = true; } return *this; } // Copy assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template <class G> constexpr expected& operator=(const breaded::unexpected<G>& other) noexcept( std::is_nothrow_assignable_v<E, const G&> && details::kNothrowUnionAssignable<E, T, const G&>) requires(std::is_assignable_v<E, const G&> && details::kUnionAssignable<E, T, const G&>) { if (hasValue_) { details::UnionAssign(error_, value_, other.error()); hasValue_ = false; } else { error_ = other.error(); } return *this; } // Move assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template <class G> constexpr expected& operator=(breaded::unexpected<G>&& other) noexcept(std::is_nothrow_assignable_v<E, G> && details::kNothrowUnionAssignable<E, T, G>) requires(std::is_assignable_v<E, G> && details::kUnionAssignable<E, T, G>) { if (hasValue_) { details::UnionAssign(error_, value_, std::move(other).error()); hasValue_ = false; } else { error_ = std::move(other).error(); } return *this; } #pragma endregion #pragma region Observers // https://en.cppreference.com/w/cpp/utility/expected/operator* constexpr const T* operator->() const noexcept { assert(hasValue_ && "Attempted to call expected::operator-> when has_value() == false"); return std::addressof(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator* constexpr T* operator->() noexcept { assert(hasValue_ && "Attempted to call expected::operator-> when has_value() == false"); return std::addressof(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr const T& operator*() const& noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return value_; } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr T& operator*() & noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return value_; } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr const T&& operator*() const&& noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr T&& operator*() && noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr explicit operator bool() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr bool has_value() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr T& value() & noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::as_const(error_)); } return value_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr const T& value() const& noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::as_const(error_)); } return value_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr T&& value() && noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::move(error_)); } return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr const T&& value() const&& noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::move(error_)); } return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E& error() & noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E& error() const& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E&& error() && noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E&& error() const&& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/value_or template <class U> requires(std::is_copy_constructible_v<T> && std::is_convertible_v<U, T>) [[nodiscard]] constexpr T value_or(U&& default_value) const& noexcept(std::is_nothrow_copy_constructible_v<T> && std::is_nothrow_convertible_v<U, T>) { if (hasValue_) { return value_; } return std::forward<U>(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/value_or template <class U> requires(std::is_move_constructible_v<T> && std::is_convertible_v<U, T>) [[nodiscard]] constexpr T value_or(U&& default_value) && noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_convertible_v<U, T>) { if (hasValue_) { return std::move(value_); } return std::forward<U>(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template <class G = E> requires(std::is_copy_constructible_v<E> && std::is_convertible_v<G, E>) [[nodiscard]] constexpr E error_or(G&& default_value) const& noexcept(std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_convertible_v<G, E>) { if (!hasValue_) { return error_; } return std::forward<G>(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template <class G = E> requires(std::is_move_constructible_v<E> && std::is_convertible_v<G, E>) [[nodiscard]] constexpr E error_or(G&& default_value) && noexcept(std::is_nothrow_move_constructible_v<E> && std::is_nothrow_convertible_v<G, E>) { if (!hasValue_) { return std::move(error_); } return std::forward<G>(default_value); } #pragma endregion #pragma region Monadic operations // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<expected&, F>::type and_then(F&& f) & requires(std::is_constructible_v<E, E&>) { return andThenImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<const expected&, F>::type and_then(F&& f) const& requires(std::is_constructible_v<E, const E&>) { return andThenImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<expected&&, F>::type and_then(F&& f) && requires(std::is_constructible_v<E, E &&>) { return andThenImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<const expected&&, F>::type and_then(F&& f) const&& requires(std::is_constructible_v<E, const E &&>) { return andThenImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<expected&, F>::type transform(F&& f) & requires(std::is_constructible_v<E, E&>) { return transformImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<const expected&, F>::type transform(F&& f) const& requires(std::is_constructible_v<E, const E&>) { return transformImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<expected&&, F>::type transform(F&& f) && requires(std::is_constructible_v<E, E &&>) { return transformImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<const expected&&, F>::type transform(F&& f) const&& requires(std::is_constructible_v<E, const E &&>) { return transformImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<expected&, F>::type or_else(F&& f) & requires(std::is_constructible_v<T, T&>) { return orElseImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<const expected&, F>::type or_else(F&& f) const& requires(std::is_constructible_v<T, const T&>) { return orElseImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<expected&&, F>::type or_else(F&& f) && requires(std::is_constructible_v<T, T &&>) { return orElseImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<const expected&&, F>::type or_else(F&& f) const&& requires(std::is_constructible_v<T, const T &&>) { return orElseImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<expected&, F>::type transform_error(F&& f) & requires(std::is_constructible_v<T, T&>) { return transformErrorImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<const expected&, F>::type transform_error(F&& f) const& requires(std::is_constructible_v<T, const T&>) { return transformErrorImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<expected&&, F>::type transform_error(F&& f) && requires(std::is_constructible_v<T, T &&>) { return transformErrorImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<const expected&&, F>::type transform_error(F&& f) const&& requires(std::is_constructible_v<T, const T &&>) { return transformErrorImpl(std::move(*this), std::forward<F>(f)); } #pragma endregion #pragma region Modifiers // https://en.cppreference.com/w/cpp/utility/expected/emplace template <class... Args> constexpr T& emplace(Args&&... args) noexcept requires(std::is_nothrow_constructible_v<T, Args...>) { destroy(); std::construct_at(std::addressof(value_), std::forward<Args>(args)...); hasValue_ = true; return value_; } // https://en.cppreference.com/w/cpp/utility/expected/emplace template <class U, class... Args> constexpr T& emplace(std::initializer_list<U> il, Args&&... args) noexcept requires(std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>) { destroy(); std::construct_at(std::addressof(value_), il, std::forward<Args>(args)...); hasValue_ = true; return value_; } // https://en.cppreference.com/w/cpp/utility/expected/swap constexpr void swap(expected& other) noexcept(std::is_nothrow_swappable_v<T> && std::is_nothrow_swappable_v<E> && details::kNothrowUnionSwappable<T, E>) requires(std::is_swappable_v<T> && std::is_swappable_v<E> && details::kUnionSwappable<T, E>) { // Prefer custom swap friend function, and use move-based std::swap as a fallback // https://en.cppreference.com/w/cpp/language/adl using std::swap; if (hasValue_) { if (other.hasValue_) { swap(value_, other.value_); } else { details::UnionSwap(error_, other.value_, std::move(other.error_), std::move(value_)); hasValue_ = false; other.hasValue_ = true; } } else { if (other.hasValue_) { other.swap(*this); } else { swap(error_, other.error_); } } } #pragma endregion Modifiers #pragma endregion Member functions #pragma region Non-member functions // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template <class T2, class E2> requires(!std::is_void_v<T2>) [[nodiscard]] friend constexpr bool operator==(const expected& lhs, const breaded::expected<T2, E2>& rhs) noexcept( noexcept(*lhs == *rhs) && noexcept(lhs.error() == rhs.error())) { if (lhs.has_value()) { if (rhs.has_value()) { return *lhs == *rhs; } else { return false; } } else { if (rhs.has_value()) { return false; } else { return lhs.error() == rhs.error(); } } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template <class T2> [[nodiscard]] friend constexpr bool operator==(const expected& x, const T2& val) noexcept(noexcept(*x == val)) { if (x.hasValue_) { return x.value_ == val; } else { return false; } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template <class E2> [[nodiscard]] friend constexpr bool operator==(const expected& x, const breaded::unexpected<E2>& e) noexcept(noexcept(x.error() == e.error())) { if (x.hasValue_) { return false; } else { return x.error_ == e.error(); } } // https://en.cppreference.com/w/cpp/utility/expected/swap2 friend constexpr void swap(expected& lhs, expected& rhs) noexcept(noexcept(lhs.swap(rhs))) requires(std::is_swappable_v<T> && std::is_swappable_v<E> && details::kUnionSwappable<T, E>) { lhs.swap(rhs); } #pragma endregion // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template <class F, class... Args> requires(std::is_invocable_v<F, Args...>) constexpr explicit expected(details::in_place_from_return_t, F&& f, Args&&... args) : value_(std::invoke(std::forward<F>(f), std::forward<Args>(args)...)), hasValue_(true) {} // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template <class F, class... Args> requires(std::is_invocable_v<F, Args...>) constexpr explicit expected(details::unexpect_from_return_t, F&& f, Args&&... args) : error_(std::invoke(std::forward<F>(f), std::forward<Args>(args)...)), hasValue_(false) {} private: template <class Other> constexpr void constructFromExpected(Other&& other) { if (other.has_value()) { std::construct_at(std::addressof(value_), *std::forward<Other>(other)); } else { std::construct_at(std::addressof(error_), std::forward<Other>(other).error()); } hasValue_ = other.has_value(); } template <class Other> constexpr void assignFromExpected(Other&& other) { if (other.has_value()) { if (hasValue_) { // If this->has_value() equals other.has_value(), assigns the value contained in other. value_ = *std::forward<Other>(other); } else { details::UnionAssign(value_, error_, *std::forward<Other>(other)); } } else { if (hasValue_) { details::UnionAssign(error_, value_, std::forward<Other>(other).error()); } else { // If this->has_value() equals other.has_value(), assigns the value contained in other. error_ = std::forward<Other>(other).error(); } } // If no exception was thrown, after assignment, has_value() is equal to other.has_value(). hasValue_ = other.has_value(); } constexpr void destroy() noexcept { if (hasValue_) { std::destroy_at(std::addressof(value_)); } else { std::destroy_at(std::addressof(error_)); } } template <class Self, class F> constexpr static AndThenReturn<Self, F>::type andThenImpl(Self&& self, F&& f) { if (self.hasValue_) { return std::invoke(std::forward<F>(f), *std::forward<Self>(self)); } else { return AndThenReturn<Self, F>::type(breaded::unexpect, std::forward<Self>(self).error()); } } template <class Self, class F> constexpr static TransformReturn<Self, F>::type transformImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformReturn<Self, F>::type(details::in_place_from_return, std::forward<F>(f), *std::forward<Self>(self)); } else { return TransformReturn<Self, F>::type(breaded::unexpect, std::forward<Self>(self).error()); } } template <class Self, class F> constexpr static OrElseReturn<Self, F>::type orElseImpl(Self&& self, F&& f) { if (self.hasValue_) { return OrElseReturn<Self, F>::type(*std::forward<Self>(self)); } else { return std::invoke(std::forward<F>(f), std::forward<Self>(self).error()); } } template <class Self, class F> constexpr static TransformErrorReturn<Self, F>::type transformErrorImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformErrorReturn<Self, F>::type(*std::forward<Self>(self)); } else { return TransformErrorReturn<Self, F>::type(details::unexpect_from_return, std::forward<F>(f), std::forward<Self>(self).error()); } } union { T value_; E error_; }; bool hasValue_; }; #pragma endregion #pragma region Expected void template <class T, class E> requires(std::is_void_v<T>) class expected<T, E> { // partial specialization of expected for void types // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L6909 static_assert(details::ValidExpectedErrorType<E>); template <typename Self, typename F> struct AndThenReturn { using type = std::remove_cvref_t<std::invoke_result_t<F>>; }; template <typename Self, typename F> struct TransformReturn { using URaw = std::invoke_result_t<F>; using U = std::remove_cvref_t<URaw>; using type = breaded::expected<U, E>; }; template <typename Self, typename F> struct OrElseReturn { using type = std::remove_cvref_t<std::invoke_result_t<F, decltype(std::declval<Self>().error())>>; }; template <typename Self, typename F> struct TransformErrorReturn { using GRaw = std::invoke_result_t<F, decltype(std::declval<Self>().error())>; using G = std::remove_cvref_t<GRaw>; using type = breaded::expected<T, G>; }; public: #pragma region Member types using value_type = T; using error_type = E; using unexpected_type = breaded::unexpected<E>; #pragma endregion #pragma region Member alias templates // Member alias templates template <class U> using rebind = breaded::expected<U, error_type>; #pragma endregion private: template <class U> static constexpr bool kExpectedConvertibleToErrorType = std::is_constructible_v<unexpected_type, U&> || std::is_constructible_v<unexpected_type, U> || std::is_constructible_v<unexpected_type, const U&> || std::is_constructible_v<unexpected_type, const U>; public: // Default empty constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected() noexcept : hasValue_(true) {} // Non-trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(const expected& other) noexcept(std::is_nothrow_copy_constructible_v<E>) requires(std::is_copy_constructible_v<E> && !std::is_trivially_copy_constructible_v<E>) { constructFromExpected(other); } // Trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(const expected& other) requires(std::is_copy_constructible_v<E> && std::is_trivially_copy_constructible_v<E>) = default; // This constructor is defined as deleted unless is_copy_constructible_v<E> is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L3373 expected(const expected&) = delete; // Non-trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(expected&& other) noexcept(std::is_nothrow_move_constructible_v<E>) requires(std::is_move_constructible_v<E> && !std::is_trivially_move_constructible_v<E>) { constructFromExpected(std::move(other)); } // Trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(expected&& other) requires(std::is_move_constructible_v<E> && std::is_trivially_move_constructible_v<E>) = default; // Converting copy constructor from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U, class G> requires(!std::is_same_v<expected, breaded::expected<U, G>> && std::is_void_v<U> && std::is_constructible_v<E, const G&> && !kExpectedConvertibleToErrorType<breaded::expected<U, G>>) constexpr explicit(!std::is_convertible_v<const G&, E>) expected(const breaded::expected<U, G>& other) noexcept(std::is_nothrow_constructible_v<E, const G&>) { constructFromExpected(other); } // Converting move constructor from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U, class G> requires(!std::is_same_v<expected, breaded::expected<U, G>> && std::is_void_v<U> && std::is_constructible_v<E, G> && !kExpectedConvertibleToErrorType<breaded::expected<U, G>>) constexpr explicit(!std::is_convertible_v<G, E>) expected(breaded::expected<U, G>&& other) noexcept(std::is_nothrow_constructible_v<E, G>) { constructFromExpected(std::move(other)); } // Copy constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class G> requires(std::is_constructible_v<E, const G&>) constexpr explicit(!std::is_convertible_v<const G&, E>) expected(const breaded::unexpected<G>& e) noexcept(std::is_nothrow_constructible_v<E, const G&>) : error_(e.error()), hasValue_(false) {} // Move constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template <class G> requires(std::is_constructible_v<E, G>) constexpr explicit(!std::is_convertible_v<G, E>) expected(breaded::unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G>) : error_(std::move(e).error()), hasValue_(false) {} // In-place empty constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr explicit expected(std::in_place_t) noexcept : hasValue_(true) {} // In-place error constructor from args // https://en.cppreference.com/w/cpp/utility/expected/expected template <class... Args> requires(std::is_constructible_v<E, Args...>) constexpr explicit expected(breaded::unexpect_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<E, Args...>) : error_(std::forward<Args>(args)...), hasValue_(false) {} // In-place error constructor from initializer list // https://en.cppreference.com/w/cpp/utility/expected/expected template <class U, class... Args> requires(std::is_constructible_v<E, std::initializer_list<U>&, Args...>) constexpr explicit expected(breaded::unexpect_t, std::initializer_list<U> il, Args&&... args) noexcept( std::is_nothrow_constructible_v<E, std::initializer_list<U>&, Args...>) : error_(il, std::forward<Args>(args)...), hasValue_(false) {} // Non-trivial destructor // https://en.cppreference.com/w/cpp/utility/expected/~expected#void_partial_specialization_destructor constexpr ~expected() noexcept { destroy(); } // Trivial destructor // https://en.cppreference.com/w/cpp/utility/expected/~expected#void_partial_specialization_destructor ~expected() requires(std::is_trivially_destructible_v<E>) = default; #pragma region Assignment // Non-trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(const expected& other) noexcept(std::is_nothrow_copy_assignable_v<E> && std::is_nothrow_copy_constructible_v<E>) requires(std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> && !std::is_trivially_copy_assignable_v<E>) { assignFromExpected(other); return *this; } // Trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(const expected& other) requires(std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E>) = default; // This operator is defined as deleted unless is_copy_assignable_v<E> is true and is_copy_constructible_v<E> is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L9007 expected& operator=(const expected&) = delete; // Non-trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(expected&& other) noexcept(std::is_nothrow_move_assignable_v<E> && std::is_nothrow_move_constructible_v<E>) requires(std::is_move_assignable_v<E> && std::is_move_constructible_v<E> && !std::is_trivially_move_assignable_v<E>) { assignFromExpected(std::move(other)); return *this; } // Trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(expected&& other) requires(std::is_move_assignable_v<E> && std::is_move_constructible_v<E> && std::is_trivially_move_assignable_v<E>) = default; // Copy assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template <class G> constexpr expected& operator=(const breaded::unexpected<G>& other) noexcept( std::is_nothrow_constructible_v<E, const G&> && std::is_nothrow_assignable_v<E, const G&>) requires(std::is_constructible_v<E, const G&> && std::is_assignable_v<E, const G&>) { if (hasValue_) { std::construct_at(std::addressof(error_), other.error()); hasValue_ = false; } else { error_ = other.error(); } return *this; } // Move assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template <class G> constexpr expected& operator=(breaded::unexpected<G>&& other) noexcept(std::is_nothrow_constructible_v<E, G> && std::is_nothrow_assignable_v<E, G>) requires(std::is_constructible_v<E, G> && std::is_assignable_v<E, G>) { if (hasValue_) { std::construct_at(std::addressof(error_), std::move(other).error()); hasValue_ = false; } else { error_ = std::move(other).error(); } return *this; } #pragma endregion #pragma region Observers // https://en.cppreference.com/w/cpp/utility/expected/operator* constexpr void operator*() const noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr explicit operator bool() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr bool has_value() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr void value() const& noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::as_const(error_)); } } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr void value() && noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::move(error_)); } } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E& error() & noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E& error() const& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E&& error() && noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E&& error() const&& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template <class G = E> requires(std::is_copy_constructible_v<E> && std::is_convertible_v<G, E>) [[nodiscard]] constexpr E error_or(G&& default_value) const& noexcept(std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_convertible_v<G, E>) { if (!hasValue_) { return error_; } return std::forward<G>(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template <class G = E> requires(std::is_move_constructible_v<E> && std::is_convertible_v<G, E>) [[nodiscard]] constexpr E error_or(G&& default_value) && noexcept(std::is_nothrow_move_constructible_v<E> && std::is_nothrow_convertible_v<G, E>) { if (!hasValue_) { return std::move(error_); } return std::forward<G>(default_value); } #pragma endregion #pragma region Monadic operations // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<expected&, F>::type and_then(F&& f) & requires(std::is_constructible_v<E, E&>) { return andThenImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<const expected&, F>::type and_then(F&& f) const& requires(std::is_constructible_v<E, const E&>) { return andThenImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<expected&&, F>::type and_then(F&& f) && requires(std::is_constructible_v<E, E &&>) { return andThenImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template <class F> constexpr AndThenReturn<const expected&&, F>::type and_then(F&& f) const&& requires(std::is_constructible_v<E, const E &&>) { return andThenImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<expected&, F>::type transform(F&& f) & requires(std::is_constructible_v<E, E&>) { return transformImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<const expected&, F>::type transform(F&& f) const& requires(std::is_constructible_v<E, const E&>) { return transformImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<expected&&, F>::type transform(F&& f) && requires(std::is_constructible_v<E, E &&>) { return transformImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template <class F> constexpr TransformReturn<const expected&&, F>::type transform(F&& f) const&& requires(std::is_constructible_v<E, const E &&>) { return transformImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<expected&, F>::type or_else(F&& f) & { return orElseImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<const expected&, F>::type or_else(F&& f) const& { return orElseImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<expected&&, F>::type or_else(F&& f) && { return orElseImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template <class F> constexpr OrElseReturn<const expected&&, F>::type or_else(F&& f) const&& { return orElseImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<expected&, F>::type transform_error(F&& f) & { return transformErrorImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<const expected&, F>::type transform_error(F&& f) const& { return transformErrorImpl(*this, std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<expected&&, F>::type transform_error(F&& f) && { return transformErrorImpl(std::move(*this), std::forward<F>(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template <class F> constexpr TransformErrorReturn<const expected&&, F>::type transform_error(F&& f) const&& { return transformErrorImpl(std::move(*this), std::forward<F>(f)); } #pragma endregion #pragma region Modifiers // https://en.cppreference.com/w/cpp/utility/expected/emplace constexpr void emplace() noexcept { destroy(); hasValue_ = true; } // https://en.cppreference.com/w/cpp/utility/expected/swap constexpr void swap(expected& other) noexcept(std::is_nothrow_swappable_v<E> && std::is_nothrow_move_constructible_v<E>) requires(std::is_swappable_v<E> && std::is_move_constructible_v<E>) { // Prefer custom swap friend function, and use move-based std::swap as a fallback // https://en.cppreference.com/w/cpp/language/adl using std::swap; if (hasValue_) { if (!other.hasValue_) { std::construct_at(std::addressof(error_), std::move(other.error_)); std::destroy_at(std::addressof(other.error_)); hasValue_ = false; other.hasValue_ = true; } } else { if (other.hasValue_) { other.swap(*this); } else { swap(error_, other.error_); } } } #pragma endregion #pragma region Non-member functions // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template <class T2, class E2> requires(std::is_void_v<T2>) [[nodiscard]] friend constexpr bool operator==(const expected& lhs, const breaded::expected<T2, E2>& rhs) noexcept( noexcept(lhs.error() == rhs.error())) { if (lhs.hasValue_) { return rhs.hasValue_; } else { if (rhs.hasValue_) { return false; } else { return lhs.error_ == rhs.error_; } } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template <class E2> [[nodiscard]] friend constexpr bool operator==(const expected& x, const breaded::unexpected<E2>& e) noexcept(noexcept(x.error() == e.error())) { if (x.hasValue_) { return false; } else { return x.error_ == e.error(); } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp [[nodiscard]] friend constexpr void swap(expected& lhs, expected& rhs) noexcept(noexcept(lhs.swap(rhs))) requires(std::is_swappable_v<E> && std::is_move_constructible_v<E>) { lhs.swap(rhs); } #pragma endregion // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template <class F, class... Args> requires(std::is_invocable_v<F, Args...>) constexpr explicit expected(details::in_place_from_return_t, F&& f, Args&&... args) : hasValue_(true) { std::invoke(std::forward<F>(f), std::forward<Args>(args)...); } // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template <class F, class... Args> requires(std::is_invocable_v<F, Args...>) constexpr explicit expected(details::unexpect_from_return_t, F&& f, Args&&... args) : error_(std::invoke(std::forward<F>(f), std::forward<Args>(args)...)), hasValue_(false) {} private: template <class Other> constexpr void constructFromExpected(Other&& other) { if (!other.has_value()) { std::construct_at(std::addressof(error_), std::forward<Other>(other).error()); } hasValue_ = other.has_value(); } template <class Other> constexpr void assignFromExpected(Other&& other) { if (other.has_value()) { std::destroy_at(std::addressof(error_)); } else { if (hasValue_) { std::construct_at(std::addressof(error_), std::forward<Other>(other).error()); } else { // If this->has_value() equals other.has_value(), assigns the value contained in other. error_ = std::forward<Other>(other).error(); } } // If no exception was thrown, after assignment, has_value() is equal to other.has_value(). hasValue_ = other.has_value(); } constexpr void destroy() noexcept { if (!hasValue_) { std::destroy_at(std::addressof(error_)); } } template <class Self, class F> constexpr static AndThenReturn<Self, F>::type andThenImpl(Self&& self, F&& f) { if (self.hasValue_) { return std::invoke(std::forward<F>(f)); } else { return AndThenReturn<Self, F>::type(breaded::unexpect, std::forward<Self>(self).error()); } } template <class Self, class F> constexpr static TransformReturn<Self, F>::type transformImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformReturn<Self, F>::type(details::in_place_from_return, std::forward<F>(f)); } else { return TransformReturn<Self, F>::type(breaded::unexpect, std::forward<Self>(self).error()); } } template <class Self, class F> constexpr static OrElseReturn<Self, F>::type orElseImpl(Self&& self, F&& f) { if (self.hasValue_) { return OrElseReturn<Self, F>::type(); } else { return std::invoke(std::forward<F>(f), std::forward<Self>(self).error()); } } template <class Self, class F> constexpr static TransformErrorReturn<Self, F>::type transformErrorImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformErrorReturn<Self, F>::type(); } else { return TransformErrorReturn<Self, F>::type(details::unexpect_from_return, std::forward<F>(f), std::forward<Self>(self).error()); } } union { E error_; }; bool hasValue_; }; #pragma endregion }; // namespace breaded