From: Ronen Friedman Date: Mon, 13 Mar 2023 08:03:09 +0000 (-0500) Subject: common: bump up tl:expected to latest version X-Git-Tag: v19.0.0~1430^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=21e5e750d57e05de7ee97aa24dc8a7b71e316a7c;p=ceph.git common: bump up tl:expected to latest version The newer version contains bug fixes and improvements. This implementation matches C++23 std::expected(including the monadic interface P2505R5), with a known caveat: using the 'map' instead of 'transform'. Signed-off-by: Ronen Friedman --- diff --git a/src/include/expected.hpp b/src/include/expected.hpp index 740c6ad24950..7fd646774b68 100644 --- a/src/include/expected.hpp +++ b/src/include/expected.hpp @@ -1,6 +1,8 @@ /// // expected - An implementation of std::expected with extensions -// Written in 2017 by Simon Brand (@TartanLlama) +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to the @@ -14,8 +16,9 @@ #ifndef TL_EXPECTED_HPP #define TL_EXPECTED_HPP -#define TL_EXPECTED_VERSION_MAJOR 0 -#define TL_EXPECTED_VERSION_MINOR 2 +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 0 +#define TL_EXPECTED_VERSION_PATCH 1 #include #include @@ -27,7 +30,6 @@ #endif #if (defined(_MSC_VER) && _MSC_VER == 1900) -/// \exclude #define TL_EXPECTED_MSVC2015 #define TL_EXPECTED_MSVC2015_CONSTEXPR #else @@ -36,79 +38,78 @@ #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) -/// \exclude #define TL_EXPECTED_GCC49 #endif #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ !defined(__clang__)) -/// \exclude #define TL_EXPECTED_GCC54 #endif #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ !defined(__clang__)) -/// \exclude #define TL_EXPECTED_GCC55 #endif +#if !defined(TL_ASSERT) +//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug +#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49) +#include +#define TL_ASSERT(x) assert(x) +#else +#define TL_ASSERT(x) +#endif +#endif + #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) // GCC < 5 doesn't support overloading on const&& for member functions -/// \exclude -#define TL_EXPECTED_NO_CONSTRR +#define TL_EXPECTED_NO_CONSTRR // GCC < 5 doesn't support some standard C++11 type traits -/// \exclude #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ std::has_trivial_copy_constructor -/// \exclude #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::has_trivial_copy_assign // This one will be different for GCC 5.7 if it's ever supported -/// \exclude #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ std::is_trivially_destructible -// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector -// for non-copyable types -#elif (defined(__GNUC__) && __GNUC__ < 8 && \ - !defined(__clang__)) +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) #ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX #define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX namespace tl { - namespace detail { - template - struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{}; +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; #ifdef _GLIBCXX_VECTOR - template - struct is_trivially_copy_constructible> - : std::is_trivially_copy_constructible{}; +template +struct is_trivially_copy_constructible> : std::false_type {}; #endif - } -} +} // namespace detail +} // namespace tl #endif -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ tl::detail::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible #else -/// \exclude #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ std::is_trivially_copy_constructible -/// \exclude #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable -/// \exclude #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ std::is_trivially_destructible #endif #if __cplusplus > 201103L -/// \exclude #define TL_EXPECTED_CXX14 #endif @@ -120,10 +121,8 @@ namespace tl { #if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ defined(TL_EXPECTED_GCC49)) -/// \exclude #define TL_EXPECTED_11_CONSTEXPR #else -/// \exclude #define TL_EXPECTED_11_CONSTEXPR constexpr #endif @@ -132,18 +131,14 @@ template class expected; #ifndef TL_MONOSTATE_INPLACE_MUTEX #define TL_MONOSTATE_INPLACE_MUTEX -/// \brief Used to represent an expected with no data class monostate {}; -/// \brief A tag type to tell expected to construct its value in-place struct in_place_t { explicit in_place_t() = default; }; -/// \brief A tag to tell expected to construct its value in-place static constexpr in_place_t in_place{}; #endif -/// Used as a wrapper to store the unexpected value template class unexpected { public: static_assert(!std::is_same::value, "E must not be void"); @@ -153,82 +148,77 @@ public: constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} - /// \returns the contained value - /// \group unexpected_value + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} + constexpr const E &value() const & { return m_val; } - /// \group unexpected_value TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } - /// \group unexpected_value TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } - /// \exclude constexpr const E &&value() const && { return std::move(m_val); } private: E m_val; }; -/// \brief Compares two unexpected objects -/// \details Simply compares lhs.value() to rhs.value() -/// \group unexpected_relop +#ifdef __cpp_deduction_guides +template unexpected(E) -> unexpected; +#endif + template constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { return lhs.value() == rhs.value(); } -/// \group unexpected_relop template constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { return lhs.value() != rhs.value(); } -/// \group unexpected_relop template constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { return lhs.value() < rhs.value(); } -/// \group unexpected_relop template constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { return lhs.value() <= rhs.value(); } -/// \group unexpected_relop template constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { return lhs.value() > rhs.value(); } -/// \group unexpected_relop template constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { return lhs.value() >= rhs.value(); } -/// Create an `unexpected` from `e`, deducing the return type -/// -/// *Example:* -/// auto e1 = tl::make_unexpected(42); -/// unexpected e2 (42); //same semantics template unexpected::type> make_unexpected(E &&e) { return unexpected::type>(std::forward(e)); } -/// \brief A tag type to tell expected to construct the unexpected value struct unexpect_t { unexpect_t() = default; }; -/// \brief A tag to tell expected to construct the unexpected value static constexpr unexpect_t unexpect{}; -/// \exclude namespace detail { -template +template [[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - throw std::forward(e); + throw std::forward(e); #else - #ifdef _MSC_VER - __assume(0); - #else - __builtin_unreachable(); - #endif + (void)e; +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif #endif } @@ -251,20 +241,58 @@ template struct conjunction : std::conditional, B>::type {}; +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; +#endif + // std::invoke from C++17 // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround -template >{}>, - int = 0> -constexpr auto invoke(Fn &&f, Args &&... args) noexcept( +template < + typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( noexcept(std::mem_fn(f)(std::forward(args)...))) -> decltype(std::mem_fn(f)(std::forward(args)...)) { return std::mem_fn(f)(std::forward(args)...); } template >{}>> -constexpr auto invoke(Fn &&f, Args &&... args) noexcept( + typename = enable_if_t>::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( noexcept(std::forward(f)(std::forward(args)...))) -> decltype(std::forward(f)(std::forward(args)...)) { return std::forward(f)(std::forward(args)...); @@ -275,9 +303,11 @@ template struct invoke_result_impl; template struct invoke_result_impl< - F, decltype(detail::invoke(std::declval(), std::declval()...), void()), + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), Us...> { - using type = decltype(detail::invoke(std::declval(), std::declval()...)); + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); }; template @@ -285,6 +315,77 @@ using invoke_result = invoke_result_impl; template using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif #endif // Trait for checking if a type is a tl::expected @@ -325,18 +426,13 @@ using is_move_constructible_or_void = is_void_or>; template -using is_copy_assignable_or_void = - is_void_or>; - +using is_copy_assignable_or_void = is_void_or>; template -using is_move_assignable_or_void = - is_void_or>; - +using is_move_assignable_or_void = is_void_or>; } // namespace detail -/// \exclude namespace detail { struct no_init_t {}; static constexpr no_init_t no_init{}; @@ -356,19 +452,19 @@ struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -387,9 +483,9 @@ struct expected_storage_base { } } union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; @@ -403,19 +499,19 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() = default; union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; @@ -444,19 +540,19 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -474,9 +570,9 @@ template struct expected_storage_base { } union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; @@ -489,19 +585,19 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -518,16 +614,22 @@ template struct expected_storage_base { } } union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; // `T` is `void`, `E` is trivially-destructible template struct expected_storage_base { - TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {} + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else + TL_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} constexpr expected_storage_base(in_place_t) : m_has_val(true) {} @@ -535,7 +637,7 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() = default; struct dummy {}; union { - dummy m_val; unexpected m_unexpect; + dummy m_val; }; bool m_has_val; }; @@ -565,7 +667,7 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -583,8 +685,8 @@ template struct expected_storage_base { } union { - char m_dummy; unexpected m_unexpect; + char m_dummy; }; bool m_has_val; }; @@ -595,7 +697,7 @@ template struct expected_operations_base : expected_storage_base { using expected_storage_base::expected_storage_base; - template void construct(Args &&... args) noexcept { + template void construct(Args &&...args) noexcept { new (std::addressof(this->m_val)) T(std::forward(args)...); this->m_has_val = true; } @@ -605,13 +707,13 @@ struct expected_operations_base : expected_storage_base { this->m_has_val = true; } - template void construct_error(Args &&... args) noexcept { + template void construct_error(Args &&...args) noexcept { new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...); this->m_has_val = false; } - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED // These assign overloads ensure that the most efficient assignment // implementation is used while maintaining the strong exception guarantee. @@ -661,12 +763,16 @@ struct expected_operations_base : expected_storage_base { auto tmp = std::move(geterr()); geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { construct(rhs.get()); } catch (...) { geterr() = std::move(tmp); throw; } +#else + construct(rhs.get()); +#endif } else { assign_common(rhs); } @@ -692,18 +798,22 @@ struct expected_operations_base : expected_storage_base { if (!this->m_has_val && rhs.m_has_val) { auto tmp = std::move(geterr()); geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { construct(std::move(rhs).get()); } catch (...) { geterr() = std::move(tmp); throw; } +#else + construct(std::move(rhs).get()); +#endif } else { assign_common(std::move(rhs)); } } - #else +#else // If exceptions are disabled then we can just copy-construct void assign(const expected_operations_base &rhs) noexcept { @@ -720,11 +830,11 @@ struct expected_operations_base : expected_storage_base { geterr().~unexpected(); construct(std::move(rhs).get()); } else { - assign_common(rhs); + assign_common(std::move(rhs)); } } - #endif +#endif // The common part of move/copy assigning template void assign_common(Rhs &&rhs) { @@ -732,7 +842,7 @@ struct expected_operations_base : expected_storage_base { if (rhs.m_has_val) { get() = std::forward(rhs).get(); } else { - destroy_val(); + destroy_val(); construct_error(std::forward(rhs).geterr()); } } else { @@ -764,9 +874,7 @@ struct expected_operations_base : expected_storage_base { } #endif - constexpr void destroy_val() { - get().~T(); - } + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } }; // This base class provides some handy member functions which can be used in @@ -783,7 +891,7 @@ struct expected_operations_base : expected_storage_base { this->m_has_val = true; } - template void construct_error(Args &&... args) noexcept { + template void construct_error(Args &&...args) noexcept { new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...); this->m_has_val = false; @@ -819,8 +927,8 @@ struct expected_operations_base : expected_storage_base { } #endif - constexpr void destroy_val() { - //no-op + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op } }; @@ -1141,32 +1249,35 @@ class expected : private detail::expected_move_assign_base, private detail::expected_delete_assign_base, private detail::expected_default_ctor_base { static_assert(!std::is_reference::value, "T must not be a reference"); - static_assert(!std::is_same>::value, + static_assert(!std::is_same::type>::value, "T must not be in_place_t"); - static_assert(!std::is_same>::value, + static_assert(!std::is_same::type>::value, "T must not be unexpect_t"); - static_assert(!std::is_same>>::value, - "T must not be unexpected"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); static_assert(!std::is_reference::value, "E must not be a reference"); T *valptr() { return std::addressof(this->m_val); } - const T *valptr() const { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } unexpected *errptr() { return std::addressof(this->m_unexpect); } - const unexpected *errptr() const { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } template ::value> * = nullptr> - U &val() { + TL_EXPECTED_11_CONSTEXPR U &val() { return this->m_val; } - unexpected &err() { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } template ::value> * = nullptr> - const U &val() const { + constexpr const U &val() const { return this->m_val; } - const unexpected &err() const { return this->m_unexpect; } + constexpr const unexpected &err() const { return this->m_unexpect; } using impl_base = detail::expected_move_assign_base; using ctor_base = detail::expected_default_ctor_base; @@ -1178,77 +1289,45 @@ public: #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - /// \group and_then - /// Carries out some operation which returns an expected on the stored object - /// if there is one. \requires `std::invoke(std::forward(f), value())` - /// returns an `expected` for some `U`. \returns Let `U` be the result - /// of `std::invoke(std::forward(f), value())`. Returns an - /// `expected`. The return value is empty if `*this` is empty, - /// otherwise the return value of `std::invoke(std::forward(f), value())` - /// is returned. - /// \synopsis template \nconstexpr auto and_then(F &&f) &; template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { return and_then_impl(*this, std::forward(f)); } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &&; template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { return and_then_impl(std::move(*this), std::forward(f)); } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &; template constexpr auto and_then(F &&f) const & { return and_then_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; template constexpr auto and_then(F &&f) const && { return and_then_impl(std::move(*this), std::forward(f)); } #endif #else - /// \group and_then - /// Carries out some operation which returns an expected on the stored object - /// if there is one. \requires `std::invoke(std::forward(f), value())` - /// returns an `expected` for some `U`. \returns Let `U` be the result - /// of `std::invoke(std::forward(f), value())`. Returns an - /// `expected`. The return value is empty if `*this` is empty, - /// otherwise the return value of `std::invoke(std::forward(f), value())` - /// is returned. - /// \synopsis template \nconstexpr auto and_then(F &&f) &; template TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) & -> decltype(and_then_impl(*this, std::forward(f))) { + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { return and_then_impl(*this, std::forward(f)); } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &&; template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype( - and_then_impl(std::move(*this), std::forward(f))) { + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &; template - constexpr auto and_then(F &&f) const & -> decltype( - and_then_impl(*this, std::forward(f))) { + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; template - constexpr auto and_then(F &&f) const && -> decltype( - and_then_impl(std::move(*this), std::forward(f))) { + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } #endif @@ -1256,66 +1335,31 @@ public: #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - /// \brief Carries out some operation on the stored object if there is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. If `U` is `void`, returns an `expected, otherwise - // returns an `expected`. If `*this` is unexpected, the - /// result is `*this`, otherwise an `expected` is constructed from the - /// return value of `std::invoke(std::forward(f), value())` and is - /// returned. - /// - /// \group map - /// \synopsis template constexpr auto map(F &&f) &; template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { return expected_map_impl(*this, std::forward(f)); } - - /// \group map - /// \synopsis template constexpr auto map(F &&f) &&; template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } - - /// \group map - /// \synopsis template constexpr auto map(F &&f) const &; template constexpr auto map(F &&f) const & { return expected_map_impl(*this, std::forward(f)); } - - /// \group map - /// \synopsis template constexpr auto map(F &&f) const &&; template constexpr auto map(F &&f) const && { return expected_map_impl(std::move(*this), std::forward(f)); } #else - /// \brief Carries out some operation on the stored object if there is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. If `U` is `void`, returns an `expected, otherwise - // returns an `expected`. If `*this` is unexpected, the - /// result is `*this`, otherwise an `expected` is constructed from the - /// return value of `std::invoke(std::forward(f), value())` and is - /// returned. - /// - /// \group map - /// \synopsis template constexpr auto map(F &&f) &; template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) map(F &&f) & { return expected_map_impl(*this, std::forward(f)); } - - /// \group map - /// \synopsis template constexpr auto map(F &&f) &&; template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) map(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } - - /// \group map - /// \synopsis template constexpr auto map(F &&f) const &; template constexpr decltype(expected_map_impl(std::declval(), std::declval())) @@ -1324,8 +1368,6 @@ public: } #ifndef TL_EXPECTED_NO_CONSTRR - /// \group map - /// \synopsis template constexpr auto map(F &&f) const &&; template constexpr decltype(expected_map_impl(std::declval(), std::declval())) @@ -1337,67 +1379,75 @@ public: #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - /// \brief Carries out some operation on the stored unexpected object if there - /// is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. If `U` is `void`, returns an `expected`, otherwise - /// returns an `expected`. If `*this` has an expected - /// value, the result is `*this`, otherwise an `expected` is constructed - /// from `make_unexpected(std::invoke(std::forward(f), value()))` and is - /// returned. - /// - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) &; + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { return map_error_impl(*this, std::forward(f)); } - - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) &&; template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { return map_error_impl(std::move(*this), std::forward(f)); } - - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) const &; template constexpr auto map_error(F &&f) const & { return map_error_impl(*this, std::forward(f)); } - - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) const &&; template constexpr auto map_error(F &&f) const && { return map_error_impl(std::move(*this), std::forward(f)); } #else - /// \brief Carries out some operation on the stored unexpected object if there - /// is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. Returns an `expected`. If `*this` has an expected - /// value, the result is `*this`, otherwise an `expected` is constructed - /// from `make_unexpected(std::invoke(std::forward(f), value()))` and is - /// returned. - /// - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) &; template TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), std::declval())) map_error(F &&f) & { return map_error_impl(*this, std::forward(f)); } - - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) &&; template TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), std::declval())) map_error(F &&f) && { return map_error_impl(std::move(*this), std::forward(f)); } - - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) const &; template constexpr decltype(map_error_impl(std::declval(), std::declval())) @@ -1406,8 +1456,6 @@ public: } #ifndef TL_EXPECTED_NO_CONSTRR - /// \group map_error - /// \synopsis template constexpr auto map_error(F &&f) const &&; template constexpr decltype(map_error_impl(std::declval(), std::declval())) @@ -1416,15 +1464,49 @@ public: } #endif #endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } - /// \brief Calls `f` if the expectd is in the unexpected state - /// \requires `F` is invokable with `E`, and `std::invoke_result_t` - /// must be void or convertible to `expcted`. - /// \effects If `*this` has a value, returns `*this`. - /// Otherwise, if `f` returns `void`, calls `std::forward(f)(E)` and returns - /// `std::nullopt`. Otherwise, returns `std::forward(f)(E)`. - /// - /// \group or_else +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { return or_else_impl(*this, std::forward(f)); } @@ -1451,19 +1533,17 @@ public: template ::value> * = nullptr> - constexpr expected(in_place_t, Args &&... args) + constexpr expected(in_place_t, Args &&...args) : impl_base(in_place, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} template &, Args &&...>::value> * = nullptr> - constexpr expected(in_place_t, std::initializer_list il, Args &&... args) + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) : impl_base(in_place, il, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} - /// \group unexpected_ctor - /// \synopsis EXPLICIT constexpr expected(const unexpected &e); template ::value> * = nullptr, @@ -1473,7 +1553,6 @@ public: : impl_base(unexpect, e.value()), ctor_base(detail::default_constructor_tag{}) {} - /// \exclude template < class G = E, detail::enable_if_t::value> * = @@ -1483,8 +1562,6 @@ public: : impl_base(unexpect, e.value()), ctor_base(detail::default_constructor_tag{}) {} - /// \group unexpected_ctor - /// \synopsis EXPLICIT constexpr expected(unexpected &&e); template < class G = E, detail::enable_if_t::value> * = nullptr, @@ -1494,7 +1571,6 @@ public: : impl_base(unexpect, std::move(e.value())), ctor_base(detail::default_constructor_tag{}) {} - /// \exclude template < class G = E, detail::enable_if_t::value> * = nullptr, @@ -1507,16 +1583,15 @@ public: template ::value> * = nullptr> - constexpr explicit expected(unexpect_t, Args &&... args) + constexpr explicit expected(unexpect_t, Args &&...args) : impl_base(unexpect, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} - /// \exclude template &, Args &&...>::value> * = nullptr> constexpr explicit expected(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : impl_base(unexpect, il, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} @@ -1531,11 +1606,10 @@ public: if (rhs.has_value()) { this->construct(*rhs); } else { - this->construct_error(rhs.error()); + this->construct_error(rhs.error()); } } - /// \exclude template ::value && std::is_convertible::value)> * = @@ -1547,8 +1621,8 @@ public: if (rhs.has_value()) { this->construct(*rhs); } else { - this->construct_error(rhs.error()); - } + this->construct_error(rhs.error()); + } } template < @@ -1561,11 +1635,10 @@ public: if (rhs.has_value()) { this->construct(std::move(*rhs)); } else { - this->construct_error(std::move(rhs.error())); - } + this->construct_error(std::move(rhs.error())); + } } - /// \exclude template < class U, class G, detail::enable_if_t<(std::is_convertible::value && @@ -1576,8 +1649,8 @@ public: if (rhs.has_value()) { this->construct(std::move(*rhs)); } else { - this->construct_error(std::move(rhs.error())); - } + this->construct_error(std::move(rhs.error())); + } } template < @@ -1587,7 +1660,6 @@ public: explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) : expected(in_place, std::forward(v)) {} - /// \exclude template < class U = T, detail::enable_if_t::value> * = nullptr, @@ -1619,7 +1691,6 @@ public: return *this; } - /// \exclude template < class U = T, class G = T, detail::enable_if_t::value> * = @@ -1639,18 +1710,18 @@ public: auto tmp = std::move(err()); err().~unexpected(); - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { - ::new (valptr()) T(std::move(v)); + ::new (valptr()) T(std::forward(v)); this->m_has_val = true; } catch (...) { err() = std::move(tmp); throw; } - #else - ::new (valptr()) T(std::move(v)); - this->m_has_val = true; - #endif +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif } return *this; @@ -1688,27 +1759,27 @@ public: template ::value> * = nullptr> - void emplace(Args &&... args) { + void emplace(Args &&...args) { if (has_value()) { - val() = T(std::forward(args)...); + val().~T(); } else { err().~unexpected(); - ::new (valptr()) T(std::forward(args)...); this->m_has_val = true; } + ::new (valptr()) T(std::forward(args)...); } - /// \exclude template ::value> * = nullptr> - void emplace(Args &&... args) { + void emplace(Args &&...args) { if (has_value()) { - val() = T(std::forward(args)...); + val().~T(); + ::new (valptr()) T(std::forward(args)...); } else { auto tmp = std::move(err()); err().~unexpected(); - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(std::forward(args)...); this->m_has_val = true; @@ -1716,17 +1787,17 @@ public: err() = std::move(tmp); throw; } - #else +#else ::new (valptr()) T(std::forward(args)...); this->m_has_val = true; - #endif +#endif } } template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&... args) { + void emplace(std::initializer_list il, Args &&...args) { if (has_value()) { T t(il, std::forward(args)...); val() = std::move(t); @@ -1737,11 +1808,10 @@ public: } } - /// \exclude template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&... args) { + void emplace(std::initializer_list il, Args &&...args) { if (has_value()) { T t(il, std::forward(args)...); val() = std::move(t); @@ -1749,7 +1819,7 @@ public: auto tmp = std::move(err()); err().~unexpected(); - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(il, std::forward(args)...); this->m_has_val = true; @@ -1757,82 +1827,160 @@ public: err() = std::move(tmp); throw; } - #else +#else ::new (valptr()) T(il, std::forward(args)...); this->m_has_val = true; - #endif +#endif } } - // TODO SFINAE - void swap(expected &rhs) noexcept( - std::is_nothrow_move_constructible::value &&noexcept( - swap(std::declval(), std::declval())) && - std::is_nothrow_move_constructible::value && - noexcept(swap(std::declval(), std::declval()))) { +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { + // swapping void is a no-op + } + + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { if (has_value() && rhs.has_value()) { - using std::swap; - swap(val(), rhs.val()); + swap_where_both_have_value(rhs, typename std::is_void::type{}); } else if (!has_value() && rhs.has_value()) { - using std::swap; - swap(err(), rhs.err()); + rhs.swap(*this); } else if (has_value()) { - auto temp = std::move(rhs.err()); - ::new (rhs.valptr()) T(val()); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); } else { - auto temp = std::move(this->err()); - ::new (valptr()) T(rhs.val()); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); + using std::swap; + swap(err(), rhs.err()); } } - /// \returns a pointer to the stored value - /// \requires a value is stored - /// \group pointer - constexpr const T *operator->() const { return valptr(); } - /// \group pointer - TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } + constexpr const T *operator->() const { + TL_ASSERT(has_value()); + return valptr(); + } + TL_EXPECTED_11_CONSTEXPR T *operator->() { + TL_ASSERT(has_value()); + return valptr(); + } - /// \returns the stored value - /// \requires a value is stored - /// \group deref template ::value> * = nullptr> constexpr const U &operator*() const & { + TL_ASSERT(has_value()); return val(); } - /// \group deref template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); return val(); } - /// \group deref template ::value> * = nullptr> constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); return std::move(val()); } - /// \group deref template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + TL_ASSERT(has_value()); return std::move(val()); } - /// \returns whether or not the optional has a value - /// \group has_value constexpr bool has_value() const noexcept { return this->m_has_val; } - /// \group has_value constexpr explicit operator bool() const noexcept { return this->m_has_val; } - /// \returns the contained value if there is one, otherwise throws - /// [bad_expected_access] - /// - /// \group value template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR const U &value() const & { @@ -1840,7 +1988,6 @@ public: detail::throw_exception(bad_expected_access(err().value())); return val(); } - /// \group value template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR U &value() & { @@ -1848,43 +1995,44 @@ public: detail::throw_exception(bad_expected_access(err().value())); return val(); } - /// \group value template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR const U &&value() const && { if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); + detail::throw_exception(bad_expected_access(std::move(err()).value())); return std::move(val()); } - /// \group value template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR U &&value() && { if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); + detail::throw_exception(bad_expected_access(std::move(err()).value())); return std::move(val()); } - /// \returns the unexpected value - /// \requires there is an unexpected value - /// \group error - constexpr const E &error() const & { return err().value(); } - /// \group error - TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } - /// \group error - constexpr const E &&error() const && { return std::move(err().value()); } - /// \group error - TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } + constexpr const E &error() const & { + TL_ASSERT(!has_value()); + return err().value(); + } + TL_EXPECTED_11_CONSTEXPR E &error() & { + TL_ASSERT(!has_value()); + return err().value(); + } + constexpr const E &&error() const && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } + TL_EXPECTED_11_CONSTEXPR E &&error() && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } - /// \returns the stored value if there is one, otherwise returns `u` - /// \group value_or template constexpr T value_or(U &&v) const & { static_assert(std::is_copy_constructible::value && std::is_convertible::value, "T must be copy-constructible and convertible to from U&&"); return bool(*this) ? **this : static_cast(std::forward(v)); } - /// \group value_or template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { static_assert(std::is_move_constructible::value && std::is_convertible::value, @@ -1893,7 +2041,6 @@ public: } }; -/// \exclude namespace detail { template using exp_t = typename detail::decay_t::value_type; template using err_t = typename detail::decay_t::error_type; @@ -1909,7 +2056,7 @@ constexpr auto and_then_impl(Exp &&exp, F &&f) { return exp.has_value() ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } template ::value, "F must return an expected"); return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } #else template struct TC; @@ -1932,7 +2079,7 @@ auto and_then_impl(Exp &&exp, F &&f) -> Ret { return exp.has_value() ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } template Ret { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } #endif #ifdef TL_EXPECTED_CXX14 template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -1960,7 +2107,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) { } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -1985,7 +2132,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) { } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), detail::enable_if_t::value> * = nullptr> auto expected_map_impl(Exp &&exp, F &&f) { @@ -1996,10 +2143,10 @@ auto expected_map_impl(Exp &&exp, F &&f) { } return result(unexpect, std::forward(exp).error()); -} +} #else template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2014,7 +2161,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2029,7 +2176,7 @@ auto expected_map_impl(Exp &&exp, F &&f) -> expected> { } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2042,7 +2189,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2053,13 +2200,13 @@ auto expected_map_impl(Exp &&exp, F &&f) -> expected> { } return unexpected>(std::forward(exp).error()); -} +} #endif #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2071,7 +2218,7 @@ constexpr auto map_error_impl(Exp &&exp, F &&f) { std::forward(exp).error())); } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2085,7 +2232,7 @@ auto map_error_impl(Exp &&exp, F &&f) { return result(unexpect, monostate{}); } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2097,7 +2244,7 @@ constexpr auto map_error_impl(Exp &&exp, F &&f) { std::forward(exp).error())); } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2109,10 +2256,10 @@ auto map_error_impl(Exp &&exp, F &&f) { detail::invoke(std::forward(f), std::forward(exp).error()); return result(unexpect, monostate{}); -} +} #else template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2127,7 +2274,7 @@ constexpr auto map_error_impl(Exp &&exp, F &&f) } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2142,7 +2289,7 @@ auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2157,7 +2304,7 @@ constexpr auto map_error_impl(Exp &&exp, F &&f) } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), detail::enable_if_t::value> * = nullptr> @@ -2169,7 +2316,7 @@ auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { detail::invoke(std::forward(f), std::forward(exp).error()); return result(unexpect, monostate{}); -} +} #endif #ifdef TL_EXPECTED_CXX14 @@ -2179,9 +2326,9 @@ template ::value> * = nullptr> constexpr auto or_else_impl(Exp &&exp, F &&f) { static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); } template ().error())), detail::enable_if_t::value> * = nullptr> detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), - std::forward(exp)); + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); } #else template (), std::declval().error())), - detail::enable_if_t::value> * = nullptr> + detail::enable_if_t::value> * = nullptr> auto or_else_impl(Exp &&exp, F &&f) -> Ret { static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); } template (), std::declval().error())), - detail::enable_if_t::value> * = nullptr> + detail::enable_if_t::value> * = nullptr> detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), - std::forward(exp)); + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); } #endif } // namespace detail @@ -2233,6 +2380,20 @@ constexpr bool operator!=(const expected &lhs, ? true : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); } +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} template constexpr bool operator==(const expected &x, const U &v) { @@ -2268,15 +2429,16 @@ constexpr bool operator!=(const unexpected &e, const expected &x) { return x.has_value() ? true : x.error() != e.value(); } -// TODO is_swappable template ::value && - std::is_move_constructible::value> * = nullptr> + detail::enable_if_t<(std::is_void::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> void swap(expected &lhs, expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } } // namespace tl -#define TL_OPTIONAL_EXPECTED_MUTEX #endif