//////////////////////////////////////////////////////////////////////////////// /// \file result.hpp /// /// \brief This header contains the 'result' monadic type for indicating /// possible error conditions //////////////////////////////////////////////////////////////////////////////// /* The MIT License (MIT) Copyright (c) 2017-2021 Matthew Rodusek All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef RESULT_RESULT_HPP #define RESULT_RESULT_HPP #include // std::size_t #include // std::enable_if, std::is_constructible, etc #include // placement-new #include // std::address_of #include // std::reference_wrapper, std::invoke #include // std::in_place_t, std::forward #include // std::initializer_list #include // std::string (for exception message) #if defined(RESULT_EXCEPTIONS_DISABLED) # include // std::fprintf, stderr #else # include // std::logic_error #endif #if __cplusplus >= 201402L # define RESULT_CPP14_CONSTEXPR constexpr #else # define RESULT_CPP14_CONSTEXPR #endif #if __cplusplus >= 201703L # define RESULT_CPP17_INLINE inline #else # define RESULT_CPP17_INLINE #endif #if defined(__clang__) && defined(_MSC_VER) # define RESULT_INLINE_VISIBILITY __attribute__((visibility("hidden"))) #elif defined(__clang__) || defined(__GNUC__) # define RESULT_INLINE_VISIBILITY __attribute__((visibility("hidden"), always_inline)) #elif defined(_MSC_VER) # define RESULT_INLINE_VISIBILITY __forceinline #else # define RESULT_INLINE_VISIBILITY #endif // [[clang::warn_unused_result]] is more full-featured than gcc's variant, since // it supports being applied to class objects. #if __cplusplus >= 201703L # define RESULT_NODISCARD [[nodiscard]] # define RESULT_WARN_UNUSED [[nodiscard]] #elif defined(__clang__) && ((__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ >= 9))) # define RESULT_NODISCARD [[clang::warn_unused_result]] # define RESULT_WARN_UNUSED [[clang::warn_unused_result]] #elif defined(__GNUC__) # define RESULT_NODISCARD # define RESULT_WARN_UNUSED [[gnu::warn_unused_result]] #else # define RESULT_WARN_UNUSED # define RESULT_NODISCARD #endif #if defined(RESULT_NAMESPACE) # define RESULT_NAMESPACE_INTERNAL RESULT_NAMESPACE #else # define RESULT_NAMESPACE_INTERNAL cpp #endif #define RESULT_NS_IMPL RESULT_NAMESPACE_INTERNAL::bitwizeshift // clang's `-Wdocumentation-unknown-command` flag is bugged and does not // understand `\copydoc` tags, despite this being a valid doxygen tag. #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdocumentation-unknown-command" #endif namespace RESULT_NAMESPACE_INTERNAL { inline namespace bitwizeshift { //=========================================================================== // utilities : constexpr forward //=========================================================================== // std::forward is not constexpr until C++14 namespace detail { #if __cplusplus >= 201402L using std::forward; #else template inline RESULT_INLINE_VISIBILITY constexpr auto forward(typename std::remove_reference::type& t) noexcept -> T&& { return static_cast(t); } template inline RESULT_INLINE_VISIBILITY constexpr auto forward(typename std::remove_reference::type&& t) noexcept -> T&& { return static_cast(t); } #endif } // namespace detail //=========================================================================== // utilities : invoke / invoke_result //=========================================================================== // std::invoke was introduced in C++17 namespace detail { #if __cplusplus >= 201703L using std::invoke; using std::invoke_result; using std::invoke_result_t; #else template struct is_reference_wrapper : std::false_type{}; template struct is_reference_wrapper> : std::true_type{}; //------------------------------------------------------------------------- template ::value && std::is_base_of::type>::value >::type> inline RESULT_INLINE_VISIBILITY constexpr auto invoke(T Base::*pmf, Derived&& ref, Args&&... args) noexcept(noexcept((::RESULT_NS_IMPL::detail::forward(ref).*pmf)(::RESULT_NS_IMPL::detail::forward(args)...))) -> decltype((::RESULT_NS_IMPL::detail::forward(ref).*pmf)(::RESULT_NS_IMPL::detail::forward(args)...)) { return (RESULT_NS_IMPL::detail::forward(ref).*pmf)(RESULT_NS_IMPL::detail::forward(args)...); } template ::value && is_reference_wrapper::type>::value >::type> inline RESULT_INLINE_VISIBILITY constexpr auto invoke(T Base::*pmf, RefWrap&& ref, Args&&... args) noexcept(noexcept((ref.get().*pmf)(std::forward(args)...))) -> decltype((ref.get().*pmf)(RESULT_NS_IMPL::detail::forward(args)...)) { return (ref.get().*pmf)(RESULT_NS_IMPL::detail::forward(args)...); } template ::value && !is_reference_wrapper::type>::value && !std::is_base_of::type>::value >::type> inline RESULT_INLINE_VISIBILITY constexpr auto invoke(T Base::*pmf, Pointer&& ptr, Args&&... args) noexcept(noexcept(((*std::forward(ptr)).*pmf)(std::forward(args)...))) -> decltype(((*RESULT_NS_IMPL::detail::forward(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward(args)...)) { return ((*RESULT_NS_IMPL::detail::forward(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward(args)...); } template ::value && std::is_base_of::type>::value >::type> inline RESULT_INLINE_VISIBILITY constexpr auto invoke(T Base::*pmd, Derived&& ref) noexcept(noexcept(std::forward(ref).*pmd)) -> decltype(RESULT_NS_IMPL::detail::forward(ref).*pmd) { return RESULT_NS_IMPL::detail::forward(ref).*pmd; } template ::value && is_reference_wrapper::type>::value >::type> inline RESULT_INLINE_VISIBILITY constexpr auto invoke(T Base::*pmd, RefWrap&& ref) noexcept(noexcept(ref.get().*pmd)) -> decltype(ref.get().*pmd) { return ref.get().*pmd; } template ::value && !is_reference_wrapper::type>::value && !std::is_base_of::type>::value >::type> inline RESULT_INLINE_VISIBILITY constexpr auto invoke(T Base::*pmd, Pointer&& ptr) noexcept(noexcept((*std::forward(ptr)).*pmd)) -> decltype((*RESULT_NS_IMPL::detail::forward(ptr)).*pmd) { return (*RESULT_NS_IMPL::detail::forward(ptr)).*pmd; } template ::type>::value>::type> inline RESULT_INLINE_VISIBILITY constexpr auto invoke(F&& f, Args&&... args) noexcept(noexcept(std::forward(f)(std::forward(args)...))) -> decltype(RESULT_NS_IMPL::detail::forward(f)(RESULT_NS_IMPL::detail::forward(args)...)) { return RESULT_NS_IMPL::detail::forward(f)(RESULT_NS_IMPL::detail::forward(args)...); } template struct is_invocable { template static auto test( Fn2&&, Args2&&... ) -> decltype(invoke(std::declval(), std::declval()...), std::true_type{}); static auto test(...) -> std::false_type; using type = decltype(test(std::declval(), std::declval()...)); static constexpr bool value = type::value; }; template struct invoke_result_impl { using type = decltype(RESULT_NS_IMPL::detail::invoke(std::declval(), std::declval()...)); }; template struct invoke_result_impl{}; template struct invoke_result : invoke_result_impl::value, Fn, Args...>{}; template using invoke_result_t = typename invoke_result::type; #endif } //=========================================================================== // struct : in_place_t //=========================================================================== #if __cplusplus >= 201703L using std::in_place_t; using std::in_place; #else /// \brief A structure for representing in-place construction struct in_place_t { explicit in_place_t() = default; }; RESULT_CPP17_INLINE constexpr auto in_place = in_place_t{}; #endif //=========================================================================== // struct : in_place_t //=========================================================================== /// \brief A structure for representing in-place construction of an error type struct in_place_error_t { explicit in_place_error_t() = default; }; RESULT_CPP17_INLINE constexpr auto in_place_error = in_place_error_t{}; //=========================================================================== // forward-declarations //=========================================================================== template class failure; template class result; template class bad_result_access; //=========================================================================== // traits //=========================================================================== template struct is_failure : std::false_type{}; template struct is_failure> : std::true_type{}; template struct is_result : std::false_type{}; template struct is_result> : std::true_type{}; //=========================================================================== // trait : detail::wrapped_result_type //=========================================================================== namespace detail { template using wrapped_result_type = typename std::conditional< std::is_lvalue_reference::value, std::reference_wrapper< typename std::remove_reference::type >, typename std::remove_const::type >::type; } // namespace detail #if !defined(RESULT_DISABLE_EXCEPTIONS) //=========================================================================== // class : bad_result_access //=========================================================================== ///////////////////////////////////////////////////////////////////////////// /// \brief An exception thrown when result::value is accessed without /// a contained value ///////////////////////////////////////////////////////////////////////////// template class bad_result_access : public std::logic_error { //------------------------------------------------------------------------- // Constructor / Assignment //------------------------------------------------------------------------- public: /// \brief Constructs this exception using the underlying error type for /// the error type /// /// \param error the underlying error template ::value>::type> explicit bad_result_access(E2&& error); /// \{ /// \brief Constructs this exception using the underlying error type for /// the error and a message /// /// \param what_arg the message for the failure /// \param error the underlying error template ::value>::type> bad_result_access(const char* what_arg, E2&& error); template ::value>::type> bad_result_access(const std::string& what_arg, E2&& error); /// \} bad_result_access(const bad_result_access& other) = default; bad_result_access(bad_result_access&& other) = default; //------------------------------------------------------------------------- auto operator=(const bad_result_access& other) -> bad_result_access& = default; auto operator=(bad_result_access&& other) -> bad_result_access& = default; /// \{ /// \brief Gets the underlying error /// /// \return the error auto error() & noexcept -> E&; auto error() && noexcept -> E&&; auto error() const & noexcept -> const E&; auto error() const && noexcept -> const E&&; /// \} //------------------------------------------------------------------------- // Private Members //------------------------------------------------------------------------- private: E m_error; }; #endif namespace detail { template using failure_is_value_convertible = std::integral_constant::value && !std::is_same::type, in_place_t>::value && !is_failure::type>::value && !is_result::type>::value )>; template using failure_is_explicit_value_convertible = std::integral_constant::value && !std::is_convertible::value )>; template using failure_is_implicit_value_convertible = std::integral_constant::value && std::is_convertible::value )>; template using failure_is_value_assignable = std::integral_constant::type>::value && !is_failure::type>::value && std::is_assignable&,E2>::value )>; } // namespace detail //=========================================================================== // class : failure_type //=========================================================================== ////////////////////////////////////////////////////////////////////////////// /// \brief A semantic type used for distinguishing failure values in an /// API that returns result types /// /// \tparam E the error type ////////////////////////////////////////////////////////////////////////////// template class failure { static_assert( !is_result::type>::value, "A (possibly CV-qualified) result 'E' type is ill-formed." ); static_assert( !is_failure::type>::value, "A (possibly CV-qualified) failure 'E' type is ill-formed." ); static_assert( !std::is_void::type>::value, "A (possibly CV-qualified) 'void' 'E' type is ill-formed." ); static_assert( !std::is_rvalue_reference::value, "rvalue references for 'E' type is ill-formed. " "Only lvalue references are valid." ); //------------------------------------------------------------------------- // Public Member Types //------------------------------------------------------------------------- public: using error_type = E; //------------------------------------------------------------------------- // Constructors / Assignment //------------------------------------------------------------------------- public: /// \brief Constructs a failure via default construction failure() = default; /// \brief Constructs a failure by delegating construction to the /// underlying constructor /// /// \param args the arguments to forward to E's constructor template ::value>::type> constexpr failure(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs a failure by delegating construction to the /// underlying constructor /// /// \param ilist the initializer list /// \param args the arguments to forward to E's constructor template ,Args...>::value>::type> constexpr failure(in_place_t, std::initializer_list ilist, Args&&...args) noexcept(std::is_nothrow_constructible, Args...>::value); /// \{ /// \brief Constructs a failure from the given error /// /// \param error the error to create a failure from template ::value,int>::type = 0> constexpr failure(E2&& error) noexcept(std::is_nothrow_constructible::value); template ::value,int>::type = 0> constexpr explicit failure(E2&& error) noexcept(std::is_nothrow_constructible::value); /// \} /// \brief Constructs this failure by copying the contents of an existing /// one /// /// \param other the other failure to copy /* implicit */ failure(const failure& other) = default; /// \brief Constructs this failure by moving the contents of an existing /// one /// /// \param other the other failure to move /* implicit */ failure(failure&& other) = default; /// \brief Constructs this failure by copy-converting \p other /// /// \param other the other failure to copy template ::value>::type> constexpr /* implicit */ failure(const failure& other) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs this failure by move-converting \p other /// /// \param other the other failure to copy template ::value>::type> constexpr /* implicit */ failure(failure&& other) noexcept(std::is_nothrow_constructible::value); //-------------------------------------------------------------------------- /// \brief Assigns the value of \p error to this failure through /// move-assignment /// /// \param error the value to assign /// \return reference to `(*this)` template ::value>::type> RESULT_CPP14_CONSTEXPR auto operator=(E2&& error) noexcept(std::is_nothrow_assignable::value || std::is_lvalue_reference::value) -> failure&; /// \brief Assigns the contents of \p other to this by copy-assignment /// /// \param other the other failure to copy /// \return reference to `(*this)` auto operator=(const failure& other) -> failure& = default; /// \brief Assigns the contents of \p other to this by move-assignment /// /// \param other the other failure to move /// \return reference to `(*this)` auto operator=(failure&& other) -> failure& = default; /// \brief Assigns the contents of \p other to this by copy conversion /// /// \param other the other failure to copy-convert /// \return reference to `(*this)` template ::value>::type> RESULT_CPP14_CONSTEXPR auto operator=(const failure& other) noexcept(std::is_nothrow_assignable::value) -> failure&; /// \brief Assigns the contents of \p other to this by move conversion /// /// \param other the other failure to move-convert /// \return reference to `(*this)` template ::value>::type> RESULT_CPP14_CONSTEXPR auto operator=(failure&& other) noexcept(std::is_nothrow_assignable::value) -> failure&; //-------------------------------------------------------------------------- // Observers //-------------------------------------------------------------------------- public: /// \{ /// \brief Gets the underlying error /// /// \return the underlying error RESULT_CPP14_CONSTEXPR auto error() & noexcept -> typename std::add_lvalue_reference::type; RESULT_CPP14_CONSTEXPR auto error() && noexcept -> typename std::add_rvalue_reference::type; constexpr auto error() const & noexcept -> typename std::add_lvalue_reference::type>::type; constexpr auto error() const && noexcept -> typename std::add_rvalue_reference::type>::type; /// \} //------------------------------------------------------------------------- // Private Member Types //------------------------------------------------------------------------- private: using underlying_type = detail::wrapped_result_type; //------------------------------------------------------------------------- // Private Members //------------------------------------------------------------------------- private: underlying_type m_failure; }; #if __cplusplus >= 201703L template failure(std::reference_wrapper) -> failure; template failure(T&&) -> failure::type>; #endif //=========================================================================== // non-member functions : class : failure //=========================================================================== //--------------------------------------------------------------------------- // Comparison //--------------------------------------------------------------------------- template constexpr auto operator==(const failure& lhs, const failure& rhs) noexcept -> bool; template constexpr auto operator!=(const failure& lhs, const failure& rhs) noexcept -> bool; template constexpr auto operator<(const failure& lhs, const failure& rhs) noexcept -> bool; template constexpr auto operator>(const failure& lhs, const failure& rhs) noexcept -> bool; template constexpr auto operator<=(const failure& lhs, const failure& rhs) noexcept -> bool; template constexpr auto operator>=(const failure& lhs, const failure& rhs) noexcept -> bool; //--------------------------------------------------------------------------- // Utilities //--------------------------------------------------------------------------- /// \brief Deduces and constructs a failure type from \p e /// /// \param e the failure value /// \return a constructed failure value template RESULT_WARN_UNUSED constexpr auto fail(E&& e) noexcept(std::is_nothrow_constructible::type,E>::value) -> failure::type>; /// \brief Deduces a failure reference from a reverence_wrapper /// /// \param e the failure value /// \return a constructed failure reference template RESULT_WARN_UNUSED constexpr auto fail(std::reference_wrapper e) noexcept -> failure; /// \brief Constructs a failure type from a series of arguments /// /// \tparam E the failure type /// \param args the arguments to forward to E's constructor /// \return a constructed failure type template ::value>::type> RESULT_WARN_UNUSED constexpr auto fail(Args&&...args) noexcept(std::is_nothrow_constructible::value) -> failure; /// \brief Constructs a failure type from an initializer list and series of /// arguments /// /// \tparam E the failure type /// \param args the arguments to forward to E's constructor /// \return a constructed failure type template ,Args...>::value>::type> RESULT_WARN_UNUSED constexpr auto fail(std::initializer_list ilist, Args&&...args) noexcept(std::is_nothrow_constructible, Args...>::value) -> failure; /// \brief Swaps the contents of two failure values /// /// \param lhs the left failure /// \param rhs the right failure template auto swap(failure& lhs, failure& rhs) #if __cplusplus >= 201703L noexcept(std::is_nothrow_swappable::value) -> void; #else noexcept(std::is_nothrow_move_constructible::value) -> void; #endif namespace detail { //========================================================================= // class : unit //========================================================================= /// \brief A standalone monostate object (effectively std::monostate). This /// exists to allow for `void` specializations struct unit {}; //========================================================================= // non-member functions : class : unit //========================================================================= constexpr auto operator==(unit, unit) noexcept -> bool { return true; } constexpr auto operator!=(unit, unit) noexcept -> bool { return false; } constexpr auto operator<(unit, unit) noexcept -> bool { return false; } constexpr auto operator>(unit, unit) noexcept -> bool { return false; } constexpr auto operator<=(unit, unit) noexcept -> bool { return true; } constexpr auto operator>=(unit, unit) noexcept -> bool { return true; } //========================================================================= // class : detail::result_union //========================================================================= /////////////////////////////////////////////////////////////////////////// /// \brief A basic utility that acts as a union containing the T and E /// types /// /// This is specialized on the case that both T and E are trivial, in which /// case `result_union` is also trivial /// /// \tparam T the value type result to be returned /// \tparam E the error type returned on failure /// \tparam IsTrivial Whether or not both T and E are trivial /////////////////////////////////////////////////////////////////////////// template ::value && std::is_trivially_destructible::value> struct result_union { //----------------------------------------------------------------------- // Public Member Types //----------------------------------------------------------------------- using underlying_value_type = wrapped_result_type; using underlying_error_type = E; //----------------------------------------------------------------------- // Constructors / Assignment //----------------------------------------------------------------------- /// \brief Constructs an empty object /// /// This is for use with conversion constructors, since it allows a /// temporary unused object to be set result_union(unit) noexcept; /// \brief Constructs the underlying value from the specified \p args /// /// \param args the arguments to forward to T's constructor template constexpr result_union(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs the underlying error from the specified \p args /// /// \param args the arguments to forward to E's constructor template constexpr result_union(in_place_error_t, Args&&...args) noexcept(std::is_nothrow_constructible::value); result_union(const result_union&) = default; result_union(result_union&&) = default; //----------------------------------------------------------------------- auto operator=(const result_union&) -> result_union& = default; auto operator=(result_union&&) -> result_union& = default; //----------------------------------------------------------------------- // Modifiers //----------------------------------------------------------------------- /// \brief A no-op for trivial types auto destroy() const noexcept -> void; //----------------------------------------------------------------------- // Public Members //----------------------------------------------------------------------- union { underlying_value_type m_value; underlying_error_type m_error; unit m_empty; }; bool m_has_value; }; //------------------------------------------------------------------------- template struct result_union { //----------------------------------------------------------------------- // Public Member Types //----------------------------------------------------------------------- using underlying_value_type = wrapped_result_type; using underlying_error_type = E; //----------------------------------------------------------------------- // Constructors / Assignment / Destructor //----------------------------------------------------------------------- /// \brief Constructs an empty object /// /// This is for use with conversion constructors, since it allows a /// temporary unused object to be set result_union(unit) noexcept; /// \brief Constructs the underlying value from the specified \p args /// /// \param args the arguments to forward to T's constructor template constexpr result_union(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs the underlying error from the specified \p args /// /// \param args the arguments to forward to E's constructor template constexpr result_union(in_place_error_t, Args&&...args) noexcept(std::is_nothrow_constructible::value); result_union(const result_union&) = default; result_union(result_union&&) = default; //----------------------------------------------------------------------- /// \brief Destroys the underlying stored object ~result_union() noexcept(std::is_nothrow_destructible::value && std::is_nothrow_destructible::value); //----------------------------------------------------------------------- auto operator=(const result_union&) -> result_union& = default; auto operator=(result_union&&) -> result_union& = default; //----------------------------------------------------------------------- // Modifiers //----------------------------------------------------------------------- /// \brief Destroys the underlying stored object auto destroy() -> void; //----------------------------------------------------------------------- // Public Members //----------------------------------------------------------------------- union { underlying_value_type m_value; underlying_error_type m_error; unit m_empty; }; bool m_has_value; }; //========================================================================= // class : result_construct_base //========================================================================= /////////////////////////////////////////////////////////////////////////// /// \brief Base class of assignment to enable construction and assignment /// /// This class is used with several pieces of construction to ensure /// trivial constructibility and assignability: /// /// * `result_trivial_copy_ctor_base` /// * `result_trivial_move_ctor_base` /// * `result_copy_assign_base` /// * `result_move_assign_base` /// /// \tparam T the value type /// \tparam E the error type /////////////////////////////////////////////////////////////////////////// template struct result_construct_base { //----------------------------------------------------------------------- // Constructors / Assignment //----------------------------------------------------------------------- /// \brief Constructs an empty object /// /// This is for use with conversion constructors, since it allows a /// temporary unused object to be set result_construct_base(unit) noexcept; /// \brief Constructs the underlying value from the specified \p args /// /// \param args the arguments to forward to T's constructor template constexpr result_construct_base(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs the underlying error from the specified \p args /// /// \param args the arguments to forward to E's constructor template constexpr result_construct_base(in_place_error_t, Args&&...args) noexcept(std::is_nothrow_constructible::value); result_construct_base(const result_construct_base&) = default; result_construct_base(result_construct_base&&) = default; auto operator=(const result_construct_base&) -> result_construct_base& = default; auto operator=(result_construct_base&&) -> result_construct_base& = default; //----------------------------------------------------------------------- // Construction / Assignment //----------------------------------------------------------------------- /// \brief Constructs the value type from \p args /// /// \note This is an implementation detail only meant to be used during /// construction /// /// \pre there is no contained value or error at the time of construction /// /// \param args the arguments to forward to T's constructor template auto construct_value(Args&&...args) noexcept(std::is_nothrow_constructible::value) -> void; /// \brief Constructs the error type from \p args /// /// \note This is an implementation detail only meant to be used during /// construction /// /// \pre there is no contained value or error at the time of construction /// /// \param args the arguments to forward to E's constructor template auto construct_error(Args&&...args) noexcept(std::is_nothrow_constructible::value) -> void; /// \brief Constructs the underlying error from the \p other result /// /// If \p other contains a value, then the T type will be /// default-constructed. /// /// \note This is an implementation detail only meant to be used during /// construction of `result` types /// /// \pre there is no contained value or error at the time of construction /// /// \param other the other result to construct template auto construct_error_from_result(Result&& other) -> void; /// \brief Constructs the underlying type from a result object /// /// \note This is an implementation detail only meant to be used during /// construction /// /// \pre there is no contained value or error at the time of construction /// /// \param other the other result to construct template auto construct_from_result(Result&& other) -> void; //----------------------------------------------------------------------- template auto assign_value(Value&& value) noexcept(std::is_nothrow_assignable::value) -> void; template auto assign_error(Error&& error) noexcept(std::is_nothrow_assignable::value) -> void; template auto assign_from_result(Result&& other) -> void; //----------------------------------------------------------------------- template auto construct_value_from_result_impl(std::true_type, ReferenceWrapper&& reference) noexcept -> void; template auto construct_value_from_result_impl(std::false_type, Value&& value) noexcept(std::is_nothrow_constructible::value) -> void; template auto assign_value_from_result_impl(std::true_type, Result&& other) -> void; template auto assign_value_from_result_impl(std::false_type, Result&& other) -> void; //----------------------------------------------------------------------- // Public Members //----------------------------------------------------------------------- using storage_type = result_union; storage_type storage; }; //========================================================================= // class : result_trivial_copy_ctor_base //========================================================================= template struct result_trivial_copy_ctor_base_impl : result_construct_base { using base_type = result_construct_base; using base_type::base_type; result_trivial_copy_ctor_base_impl(const result_trivial_copy_ctor_base_impl& other) noexcept(std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_constructible::value); result_trivial_copy_ctor_base_impl(result_trivial_copy_ctor_base_impl&& other) = default; auto operator=(const result_trivial_copy_ctor_base_impl& other) -> result_trivial_copy_ctor_base_impl& = default; auto operator=(result_trivial_copy_ctor_base_impl&& other) -> result_trivial_copy_ctor_base_impl& = default; }; template using conditionally_nest_type = typename std::conditional< Condition, typename Base::base_type, Base >::type; template using result_trivial_copy_ctor_base = conditionally_nest_type< std::is_trivially_copy_constructible::value && std::is_trivially_copy_constructible::value, result_trivial_copy_ctor_base_impl >; //========================================================================= // class : result_trivial_move_ctor_base //========================================================================= template struct result_trivial_move_ctor_base_impl : result_trivial_copy_ctor_base { using base_type = result_trivial_copy_ctor_base; using base_type::base_type; result_trivial_move_ctor_base_impl(const result_trivial_move_ctor_base_impl& other) = default; result_trivial_move_ctor_base_impl(result_trivial_move_ctor_base_impl&& other) noexcept(std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value); auto operator=(const result_trivial_move_ctor_base_impl& other) -> result_trivial_move_ctor_base_impl& = default; auto operator=(result_trivial_move_ctor_base_impl&& other) -> result_trivial_move_ctor_base_impl& = default; }; template using result_trivial_move_ctor_base = conditionally_nest_type< std::is_trivially_move_constructible::value && std::is_trivially_move_constructible::value, result_trivial_move_ctor_base_impl >; //========================================================================= // class : result_trivial_copy_assign_base //========================================================================= template struct result_trivial_copy_assign_base_impl : result_trivial_move_ctor_base { using base_type = result_trivial_move_ctor_base; using base_type::base_type; result_trivial_copy_assign_base_impl(const result_trivial_copy_assign_base_impl& other) = default; result_trivial_copy_assign_base_impl(result_trivial_copy_assign_base_impl&& other) = default; auto operator=(const result_trivial_copy_assign_base_impl& other) noexcept(std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_assignable::value) -> result_trivial_copy_assign_base_impl&; auto operator=(result_trivial_copy_assign_base_impl&& other) -> result_trivial_copy_assign_base_impl& = default; }; template using result_trivial_copy_assign_base = conditionally_nest_type< std::is_trivially_copy_constructible::value && std::is_trivially_copy_constructible::value && std::is_trivially_copy_assignable::value && std::is_trivially_copy_assignable::value && std::is_trivially_destructible::value && std::is_trivially_destructible::value, result_trivial_copy_assign_base_impl >; //========================================================================= // class : result_trivial_move_assign_base //========================================================================= template struct result_trivial_move_assign_base_impl : result_trivial_copy_assign_base { using base_type = result_trivial_copy_assign_base; using base_type::base_type; result_trivial_move_assign_base_impl(const result_trivial_move_assign_base_impl& other) = default; result_trivial_move_assign_base_impl(result_trivial_move_assign_base_impl&& other) = default; auto operator=(const result_trivial_move_assign_base_impl& other) -> result_trivial_move_assign_base_impl& = default; auto operator=(result_trivial_move_assign_base_impl&& other) noexcept(std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value && std::is_nothrow_move_assignable::value) -> result_trivial_move_assign_base_impl&; }; template using result_trivial_move_assign_base = conditionally_nest_type< std::is_trivially_move_constructible::value && std::is_trivially_move_constructible::value && std::is_trivially_move_assignable::value && std::is_trivially_move_assignable::value && std::is_trivially_destructible::value && std::is_trivially_destructible::value, result_trivial_move_assign_base_impl >; //========================================================================= // class : disable_copy_ctor //========================================================================= template struct disable_copy_ctor : result_trivial_move_assign_base { using base_type = result_trivial_move_assign_base; using base_type::base_type; disable_copy_ctor(const disable_copy_ctor& other) = delete; disable_copy_ctor(disable_copy_ctor&& other) = default; auto operator=(const disable_copy_ctor& other) -> disable_copy_ctor& = default; auto operator=(disable_copy_ctor&& other) -> disable_copy_ctor& = default; }; template using result_copy_ctor_base = conditionally_nest_type< std::is_copy_constructible::value && std::is_copy_constructible::value, disable_copy_ctor >; //========================================================================= // class : disable_move_ctor //========================================================================= template struct disable_move_ctor : result_copy_ctor_base { using base_type = result_copy_ctor_base; using base_type::base_type; disable_move_ctor(const disable_move_ctor& other) = default; disable_move_ctor(disable_move_ctor&& other) = delete; auto operator=(const disable_move_ctor& other) -> disable_move_ctor& = default; auto operator=(disable_move_ctor&& other) -> disable_move_ctor& = default; }; template using result_move_ctor_base = conditionally_nest_type< std::is_move_constructible::value && std::is_move_constructible::value, disable_move_ctor >; //========================================================================= // class : disable_move_assignment //========================================================================= template struct disable_move_assignment : result_move_ctor_base { using base_type = result_move_ctor_base; using base_type::base_type; disable_move_assignment(const disable_move_assignment& other) = default; disable_move_assignment(disable_move_assignment&& other) = default; auto operator=(const disable_move_assignment& other) -> disable_move_assignment& = delete; auto operator=(disable_move_assignment&& other) -> disable_move_assignment& = default; }; template using result_copy_assign_base = conditionally_nest_type< std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_constructible::value && std::is_copy_assignable>::value && std::is_copy_assignable::value, disable_move_assignment >; //========================================================================= // class : disable_copy_assignment //========================================================================= template struct disable_copy_assignment : result_copy_assign_base { using base_type = result_copy_assign_base; using base_type::base_type; disable_copy_assignment(const disable_copy_assignment& other) = default; disable_copy_assignment(disable_copy_assignment&& other) = default; auto operator=(const disable_copy_assignment& other) -> disable_copy_assignment& = default; auto operator=(disable_copy_assignment&& other) -> disable_copy_assignment& = delete; }; template using result_move_assign_base = conditionally_nest_type< std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value && std::is_move_assignable>::value && std::is_move_assignable::value, disable_copy_assignment >; //========================================================================= // alias : result_storage //========================================================================= template using result_storage = result_move_assign_base; //========================================================================= // traits : result //========================================================================= template using result_is_convertible = std::integral_constant std::is_constructible&>:: value || std::is_constructible&>:: value || std::is_constructible&&>:: value || std::is_constructible&&>:: value || // E1 constructible from result std::is_constructible&>:: value || std::is_constructible&>:: value || std::is_constructible&&>:: value || std::is_constructible&&>:: value || // result convertible to T1 std::is_convertible&, T1>:: value || std::is_convertible&, T1>:: value || std::is_convertible&&, T1>::value || std::is_convertible&&, T1>::value || // result convertible to E2 std::is_convertible&, E1>:: value || std::is_convertible&, E1>:: value || std::is_convertible&&, E1>::value || std::is_convertible&&, E1>::value )>; //------------------------------------------------------------------------- template using result_is_copy_convertible = std::integral_constant::value && std::is_constructible::value && std::is_constructible::value )>; template using result_is_implicit_copy_convertible = std::integral_constant::value && std::is_convertible::value && std::is_convertible::value )>; template using result_is_explicit_copy_convertible = std::integral_constant::value && (!std::is_convertible::value || !std::is_convertible::value) )>; //------------------------------------------------------------------------- template using result_is_move_convertible = std::integral_constant::value && std::is_constructible::value && std::is_constructible::value )>; template using result_is_implicit_move_convertible = std::integral_constant::value && std::is_convertible::value && std::is_convertible::value )>; template using result_is_explicit_move_convertible = std::integral_constant::value && (!std::is_convertible::value || !std::is_convertible::value) )>; //------------------------------------------------------------------------- template using result_is_value_convertible = std::integral_constant::value && !std::is_same::type, in_place_t>::value && !std::is_same::type, in_place_error_t>::value && !is_result::type>::value )>; template using result_is_explicit_value_convertible = std::integral_constant::value && !std::is_convertible::value )>; template using result_is_implicit_value_convertible = std::integral_constant::value && std::is_convertible::value )>; //------------------------------------------------------------------------- template using result_is_convert_assignable = std::integral_constant::value && std::is_assignable&>::value && std::is_assignable&>::value && std::is_assignable&&>::value && std::is_assignable&&>::value && std::is_assignable&>::value && std::is_assignable&>::value && std::is_assignable&&>::value && std::is_assignable&&>::value )>; template using result_is_copy_convert_assignable = std::integral_constant::value && std::is_nothrow_constructible::value && std::is_assignable&, const T2&>::value && std::is_nothrow_constructible::value && std::is_assignable::value )>; template using result_is_move_convert_assignable = std::integral_constant::value && std::is_nothrow_constructible::value && std::is_assignable::value && std::is_nothrow_constructible::value && std::is_assignable::value )>; //------------------------------------------------------------------------- template using result_is_value_assignable = std::integral_constant::type>::value && !is_failure::type>::value && std::is_nothrow_constructible::value && std::is_assignable&,U>::value && ( !std::is_same::type,typename std::decay::type>::value || !std::is_scalar::value ) )>; template using result_is_failure_assignable = std::integral_constant::value && std::is_assignable::value )>; // Friending 'extract_error" below was causing some compilers to incorrectly // identify `exp.m_storage.m_error` as being an access violation despite the // friendship. Using a type name instead seems to be ubiquitous across // compilers struct result_error_extractor { template static constexpr auto get(const result& exp) noexcept -> const E&; template static constexpr auto get(result& exp) noexcept -> E&; }; template constexpr auto extract_error(const result& exp) noexcept -> const E&; template [[noreturn]] auto throw_bad_result_access(E&& error) -> void; template [[noreturn]] auto throw_bad_result_access_message(String&& message, E&& error) -> void; } // namespace detail ///////////////////////////////////////////////////////////////////////////// /// \brief The class template `result` manages result results from APIs, /// while encoding possible failure conditions. /// /// A common use-case for result is the return value of a function that /// may fail. As opposed to other approaches, such as `std::pair` /// or `std::optional`, `result` more accurately conveys the intent of the /// user along with the failure condition to the caller. This effectively /// produces an orthogonal error handling mechanism that allows for exception /// safety while also allowing discrete testability of the return type. /// /// `result` types may contain a `T` value, which signifies that an /// operation succeeded in producing the result value of type `T`. If an /// `result` does not contain a `T` value, it will always contain an `E` /// error condition instead. /// /// An `result` can always be queried for a possible error case by /// calling the `error()` function, even if it contains a value. /// In the case that a `result` contains a value object, this will /// simply return an `E` object constructed through default aggregate /// construction, as if through the expression `E{}`, which is assumed to be /// a "valid" (no-error) state for an `E` type. /// For example: /// /// * `std::error_code{}` produces a default-construct error-code, which is /// the "no error" state, /// * integral (or enum) error codes produce a `0` value (no error), thanks to /// zero-initialization, /// * `std::exception_ptr{}` produces a null-pointer, /// * `std::string{}` produces an empty string `""`, /// * etc. /// /// When a `result` contains either a value or error, the storage for /// that object is guaranteed to be allocated as part of the result /// object's footprint, i.e. no dynamic memory allocation ever takes place. /// Thus, a result object models an object, not a pointer, even though the /// `operator*()` and `operator->()` are defined. /// /// When an object of type `result` is contextually converted to /// `bool`, the conversion returns `true` if the object contains a value and /// `false` if it contains an error. /// /// `result` objects do not have a "valueless" state like `variant`s do. /// Once a `result` has been constructed with a value or error, the /// active underlying type can only be changed through assignment which may /// is only enabled if construction is guaranteed to be *non-throwing*. This /// ensures that a valueless state cannot occur naturally. /// /// Example Use: /// \code /// auto to_string(int x) -> result /// { /// try { /// return std::stoi(x); /// } catch (const std::invalid_argument&) { /// return fail(std::errc::invalid_argument); /// } catch (const std::std::out_of_range&) { /// return fail(std::errc::result_out_of_range); /// } /// } /// \endcode /// /// \note If using C++17 or above, `fail` can be replaced with /// `failure{...}` thanks to CTAD. /// /// \tparam T the underlying value type /// \tparam E the underlying error type /////////////////////////////////////////////////////////////////////////// template class RESULT_NODISCARD result { // Type requirements static_assert( !std::is_abstract::value, "It is ill-formed for T to be abstract type" ); static_assert( !std::is_same::type, in_place_t>::value, "It is ill-formed for T to be a (possibly CV-qualified) in_place_t type" ); static_assert( !is_result::type>::value, "It is ill-formed for T to be a (possibly CV-qualified) 'result' type" ); static_assert( !is_failure::type>::value, "It is ill-formed for T to be a (possibly CV-qualified) 'failure' type" ); static_assert( !std::is_rvalue_reference::value, "It is ill-formed for T to be an rvalue 'result type. " "Only lvalue references are valid." ); static_assert( !std::is_abstract::value, "It is ill-formed for E to be abstract type" ); static_assert( !std::is_void::type>::value, "It is ill-formed for E to be (possibly CV-qualified) void type" ); static_assert( !is_result::type>::value, "It is ill-formed for E to be a (possibly CV-qualified) 'result' type" ); static_assert( !is_failure::type>::value, "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type" ); static_assert( !std::is_same::type, in_place_t>::value, "It is ill-formed for E to be a (possibly CV-qualified) in_place_t type" ); static_assert( !std::is_reference::value, "It is ill-formed for E to be a reference type. " "Only T types may be lvalue references" ); // Friendship friend detail::result_error_extractor; template friend class result; //------------------------------------------------------------------------- // Public Member Types //------------------------------------------------------------------------- public: using value_type = T; ///< The value type of this result using error_type = E; ///< The error type of this result using failure_type = failure; ///< The failure type template using rebind = result; ///< Rebinds the result type //------------------------------------------------------------------------- // Constructor / Destructor / Assignment //------------------------------------------------------------------------- public: /// \brief Default-constructs a result with the underlying value type /// active /// /// This constructor is only enabled if `T` is default-constructible /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// assert(cpp::result{} == std::string{}); /// ``` template ::value>::type> constexpr result() noexcept(std::is_nothrow_constructible::value); /// \brief Copy constructs this result /// /// If \p other contains a value, initializes the contained value as if /// direct-initializing (but not direct-list-initializing) an object of /// type `T` with the expression *other. /// /// If other contains an error, constructs an object that contains a copy /// of that error. /// /// \note This constructor is defined as deleted if /// `std::is_copy_constructible::value` or /// `std::is_copy_constructible::value` is `false` /// /// \note This constructor is defined as trivial if both /// `std::is_trivially_copy_constructible::value` and /// `std::is_trivially_copy_constructible::value` are `true` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// const auto r = cpp::result{42}; /// const auto s = r; /// /// assert(r == s); /// ``` /// /// \param other the result to copy constexpr result(const result& other) = default; /// \brief Move constructs a result /// /// If other contains a value, initializes the contained value as if /// direct-initializing (but not direct-list-initializing) an object /// of type T with the expression `std::move(*other)` and does not make /// other empty: a moved-from result still contains a value, but the /// value itself is moved from. /// /// If other contains an error, move-constructs this result from that /// error. /// /// \note This constructor is defined as deleted if /// `std::is_move_constructible::value` or /// `std::is_move_constructible::value` is `false` /// /// \note This constructor is defined as trivial if both /// `std::is_trivially_move_constructible::value` and /// `std::is_trivially_move_constructible::value` are `true` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{"hello world"}; /// auto s = std::move(r); /// /// assert(s == "hello world"); /// ``` /// /// \param other the result to move constexpr result(result&& other) = default; /// \{ /// \brief Converting copy constructor /// /// If \p other contains a value, constructs a result object /// that contains a value, initialized as if direct-initializing /// (but not direct-list-initializing) an object of type `T` with the /// expression `*other`. /// /// If \p other contains an error, constructs a result object that /// contains an error, initialized as if direct-initializing /// (but not direct-list-initializing) an object of type `E`. /// /// \note This constructor does not participate in overload resolution /// unless the following conditions are met: /// - `std::is_constructible_v` is `true` /// - T is not constructible or convertible from any expression /// of type (possibly const) `result` /// - E is not constructible or convertible from any expression /// of type (possible const) `result` /// /// \note This constructor is explicit if and only if /// `std::is_convertible_v` or /// `std::is_convertible_v` is `false` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// const auto r = cpp::result{42}; /// const auto s = cpp::result{r}; /// /// assert(r == s); /// ``` /// /// \param other the other type to convert template ::value,int>::type = 0> result(const result& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value); template ::value,int>::type = 0> explicit result(const result& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value); /// \} /// \{ /// \brief Converting move constructor /// /// If \p other contains a value, constructs a result object /// that contains a value, initialized as if direct-initializing /// (but not direct-list-initializing) an object of type T with the /// expression `std::move(*other)`. /// /// If \p other contains an error, constructs a result object that /// contains an error, initialized as if direct-initializing /// (but not direct-list-initializing) an object of type E&&. /// /// \note This constructor does not participate in overload resolution /// unless the following conditions are met: /// - `std::is_constructible_v` is `true` /// - T is not constructible or convertible from any expression /// of type (possibly const) `result` /// - E is not constructible or convertible from any expression /// of type (possible const) `result` /// /// \note This constructor is explicit if and only if /// `std::is_convertible_v` or /// `std::is_convertible_v` is `false` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result,int>{ /// std::make_unique() /// }; /// const auto s = cpp::result,long>{ /// std::move(r) /// }; /// ``` /// /// \param other the other type to convert template ::value,int>::type = 0> result(result&& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value); template ::value,int>::type = 0> explicit result(result&& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value); /// \} //------------------------------------------------------------------------- /// \brief Constructs a result object that contains a value /// /// The value is initialized as if direct-initializing (but not /// direct-list-initializing) an object of type `T` from the arguments /// `std::forward(args)...` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// cpp::in_place, "Hello world" /// }; /// ``` /// /// \param args the arguments to pass to T's constructor template ::value>::type> constexpr explicit result(in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs a result object that contains a value /// /// The value is initialized as if direct-initializing (but not /// direct-list-initializing) an object of type `T` from the arguments /// `std::forward>(ilist)`, /// `std::forward(args)...` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// cpp::in_place, {'H','e','l','l','o'} /// }; /// ``` /// /// \param ilist An initializer list of entries to forward /// \param args the arguments to pass to T's constructor template &, Args...>::value>::type> constexpr explicit result(in_place_t, std::initializer_list ilist, Args&&...args) noexcept(std::is_nothrow_constructible, Args...>::value); //------------------------------------------------------------------------- /// \brief Constructs a result object that contains an error /// /// the value is initialized as if direct-initializing (but not /// direct-list-initializing) an object of type `E` from the arguments /// `std::forward(args)...` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// cpp::in_place_error, "Hello world" /// }; /// ``` /// /// \param args the arguments to pass to E's constructor template ::value>::type> constexpr explicit result(in_place_error_t, Args&&... args) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs a result object that contains an error /// /// The value is initialized as if direct-initializing (but not /// direct-list-initializing) an object of type `E` from the arguments /// `std::forward>(ilist)`, /// `std::forward(args)...` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// cpp::in_place_error, {'H','e','l','l','o'} /// }; /// ``` /// /// \param ilist An initializer list of entries to forward /// \param args the arguments to pass to Es constructor template &, Args...>::value>::type> constexpr explicit result(in_place_error_t, std::initializer_list ilist, Args&&...args) noexcept(std::is_nothrow_constructible, Args...>::value); //------------------------------------------------------------------------- /// \{ /// \brief Constructs the underlying error of this result /// /// \note This constructor only participates in overload resolution if /// `E` is constructible from \p e /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// cpp::result r = cpp::fail(42); /// /// auto get_error_result() -> cpp::result { /// return cpp::fail("hello world!"); /// } /// ``` /// /// \param e the failure error template ::value>::type> constexpr /* implicit */ result(const failure& e) noexcept(std::is_nothrow_constructible::value); template ::value>::type> constexpr /* implicit */ result(failure&& e) noexcept(std::is_nothrow_constructible::value); /// \} /// \{ /// \brief Constructs a result object that contains a value /// /// The value is initialized as if direct-initializing (but not /// direct-list-initializing) an object of type T with the expression /// value. /// /// \note This constructor is constexpr if the constructor of T /// selected by direct-initialization is constexpr /// /// \note This constructor does not participate in overload /// resolution unless `std::is_constructible_v` is true /// and `decay_t` is neither `in_place_t`, `in_place_error_t`, /// nor a `result` type. /// /// \note This constructor is explicit if and only if /// `std::is_convertible_v` is `false` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// cpp::result r = 42; /// /// auto get_value() -> cpp::result { /// return "hello world!"; // implicit conversion /// } /// ``` /// /// \param value the value to copy template ::value,int>::type = 0> constexpr explicit result(U&& value) noexcept(std::is_nothrow_constructible::value); template ::value,int>::type = 0> constexpr /* implicit */ result(U&& value) noexcept(std::is_nothrow_constructible::value); /// \} //------------------------------------------------------------------------- /// \brief Copy assigns the result stored in \p other /// /// \note This assignment operator only participates in overload resolution /// if the following conditions are met: /// - `std::is_nothrow_copy_constructible_v` is `true`, and /// - `std::is_nothrow_copy_constructible_v` is `true` /// this restriction guarantees that no ' /// /// \note This assignment operator is defined as trivial if the following /// conditions are all `true`: /// - `std::is_trivially_copy_constructible::value` /// - `std::is_trivially_copy_constructible::value` /// - `std::is_trivially_copy_assignable::value` /// - `std::is_trivially_copy_assignable::value` /// - `std::is_trivially_destructible::value` /// - `std::is_trivially_destructible::value` /// /// \param other the other result to copy auto operator=(const result& other) -> result& = default; /// \brief Move assigns the result stored in \p other /// /// \note This assignment operator only participates in overload resolution /// if the following conditions are met: /// - `std::is_nothrow_copy_constructible_v` is `true`, and /// - `std::is_nothrow_copy_constructible_v` is `true` /// this restriction guarantees that no 'valueless_by_exception` state /// may occur. /// /// \note This assignment operator is defined as trivial if the following /// conditions are all `true`: /// - `std::is_trivially_move_constructible::value` /// - `std::is_trivially_move_constructible::value` /// - `std::is_trivially_move_assignable::value` /// - `std::is_trivially_move_assignable::value` /// - `std::is_trivially_destructible::value` /// - `std::is_trivially_destructible::value` /// /// \param other the other result to copy auto operator=(result&& other) -> result& = default; /// \brief Copy-converts the state of \p other /// /// If both `*this` and \p other contain either values or errors, the /// underlying value is constructed as if through assignment. /// /// Otherwise if `*this` contains a value, but \p other contains an error, /// then the contained value is destroyed by calling its destructor. `*this` /// will no longer contain a value after the call, and will now contain `E` /// constructed as if direct-initializing (but not direct-list-initializing) /// an object with an argument of `const E2&`. /// /// If \p other contains a value and `*this` contains an error, then the /// contained error is destroyed by calling its destructor. `*this` now /// contains a value constructed as if direct-initializing (but not /// direct-list-initializing) an object with an argument of `const T2&`. /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_constructible_v`, /// `std::is_assignable_v`, /// `std::is_nothrow_constructible_v`, /// `std::is_assignable_v` are all true. /// - T is not constructible, convertible, or assignable from any /// expression of type (possibly const) `result` /// /// \param other the other result object to convert /// \return reference to `(*this)` template ::value>::type> auto operator=(const result& other) noexcept(std::is_nothrow_assignable::value && std::is_nothrow_assignable::value) -> result&; /// \brief Move-converts the state of \p other /// /// If both `*this` and \p other contain either values or errors, the /// underlying value is constructed as if through move-assignment. /// /// Otherwise if `*this` contains a value, but \p other contains an error, /// then the contained value is destroyed by calling its destructor. `*this` /// will no longer contain a value after the call, and will now contain `E` /// constructed as if direct-initializing (but not direct-list-initializing) /// an object with an argument of `E2&&`. /// /// If \p other contains a value and `*this` contains an error, then the /// contained error is destroyed by calling its destructor. `*this` now /// contains a value constructed as if direct-initializing (but not /// direct-list-initializing) an object with an argument of `T2&&`. /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_constructible_v`, /// `std::is_assignable_v`, /// `std::is_nothrow_constructible_v`, /// `std::is_assignable_v` are all true. /// - T is not constructible, convertible, or assignable from any /// expression of type (possibly const) `result` /// /// \param other the other result object to convert /// \return reference to `(*this)` template ::value>::type> auto operator=(result&& other) noexcept(std::is_nothrow_assignable::value && std::is_nothrow_assignable::value) -> result&; /// \brief Perfect-forwarded assignment /// /// Depending on whether `*this` contains a value before the call, the /// contained value is either direct-initialized from std::forward(value) /// or assigned from std::forward(value). /// /// \note The function does not participate in overload resolution unless /// - `std::decay_t` is not a result type, /// - `std::decay_t` is not a failure type /// - `std::is_nothrow_constructible_v` is `true` /// - `std::is_assignable_v` is `true` /// - and at least one of the following is `true`: /// - `T` is not a scalar type; /// - `std::decay_t` is not `T`. /// /// \param value to assign to the contained value /// \return reference to `(*this)` template ::value>::type> auto operator=(U&& value) noexcept(std::is_nothrow_assignable::value) -> result&; /// \{ /// \brief Perfect-forwarded assignment /// /// Depending on whether `*this` contains an error before the call, the /// contained error is either direct-initialized via forwarding the error, /// or assigned from forwarding the error /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_constructible_v` is `true`, and /// - `std::is_assignable_v` is `true` /// /// \param other the failure value to assign to this /// \return reference to `(*this)` template ::value>::type> auto operator=(const failure& other) noexcept(std::is_nothrow_assignable::value) -> result&; template ::value>::type> auto operator=(failure&& other) noexcept(std::is_nothrow_assignable::value) -> result&; /// \} //------------------------------------------------------------------------- // Observers //------------------------------------------------------------------------- public: /// \{ /// \brief Retrieves a pointer to the contained value /// /// This operator exists to give `result` an `optional`-like API for cases /// where it's known that the `result` already contains a value. /// /// Care must be taken to ensure that this is only used in safe contexts /// where a `T` value is active. /// /// \note The behavior is undefined if `*this` does not contain a value. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// make_widget() /// }; /// /// r->do_something(); /// ``` /// /// \return a pointer to the contained value RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto operator->() noexcept -> typename std::remove_reference::type*; RESULT_WARN_UNUSED constexpr auto operator->() const noexcept -> typename std::remove_reference::type>::type*; /// \} /// \{ /// \brief Retrieves a reference to the contained value /// /// This operator exists to give `result` an `optional`-like API for cases /// where it's known that the `result` already contains a value. /// /// Care must be taken to ensure that this is only used in safe contexts /// where a `T` value is active. /// /// \note The behaviour is undefined if `*this` does not contain a value /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// make_widget() /// }; /// /// (*r).do_something(); /// /// consume(*r); /// ``` /// /// \return a reference to the contained value RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto operator*() & noexcept -> typename std::add_lvalue_reference::type; RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto operator*() && noexcept -> typename std::add_rvalue_reference::type; RESULT_WARN_UNUSED constexpr auto operator*() const& noexcept -> typename std::add_lvalue_reference::type>::type; RESULT_WARN_UNUSED constexpr auto operator*() const&& noexcept -> typename std::add_rvalue_reference::type>::type; /// \} //------------------------------------------------------------------------- /// \brief Contextually convertible to `true` if `*this` contains a value /// /// This function exists to allow for simple, terse checks for containing /// a value. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto get_result() -> cpp::result; /// auto r = get_result(); /// if (r) { ... } /// /// assert(static_cast(cpp::result{42})); /// /// assert(!static_cast(cpp::result{cpp::fail(42)})); /// ``` /// /// \return `true` if `*this` contains a value, `false` if `*this` /// does not contain a value RESULT_WARN_UNUSED constexpr explicit operator bool() const noexcept; /// \brief Returns `true` if `*this` contains a value /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto get_result() -> cpp::result; /// auto r = get_result(); /// if (r.has_value()) { ... } /// /// assert(cpp::result{42}.has_value()); /// /// assert(!cpp::result{cpp::fail(42)}.has_value()); /// ``` /// /// \return `true` if `*this` contains a value, `false` if `*this` /// contains an error RESULT_WARN_UNUSED constexpr auto has_value() const noexcept -> bool; /// \brief Returns `true` if `*this` contains an error /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto get_result() -> cpp::result; /// /// auto r = get_result(); /// if (r.has_error()) { ... } /// /// assert(!cpp::result{42}.has_error()); /// /// assert(cpp::result{cpp::fail(42)}.has_error()); /// ``` /// /// \return `true` if `*this` contains an error, `false` if `*this` /// contains a value RESULT_WARN_UNUSED constexpr auto has_error() const noexcept -> bool; //------------------------------------------------------------------------- /// \{ /// \brief Returns a reference to the contained value /// /// This function provides checked (throwing) access to the underlying /// value. The constness and refness of this result is propagated to the /// underlying reference. /// /// If this contains an error, an exception is thrown containing the /// underlying error. The error is consumed propagating the same constness /// and refness of this result. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// assert(cpp::result{42}.value() == 42); /// /// auto r = cpp::result,int>{ /// std::make_unique(42) /// }; /// auto s = std::move(r).value(); /// /// try { /// auto r = cpp::result{ cpp::fail(42) }; /// auto v = r.value(); /// } catch (const cpp::bad_result_access& e) { /// assert(e.error() == 42); /// } /// ``` /// /// \throws bad_result_access if `*this` does not contain a value. /// /// \return the value of `*this` RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto value() & -> typename std::add_lvalue_reference::type; RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto value() && -> typename std::add_rvalue_reference::type; RESULT_WARN_UNUSED constexpr auto value() const & -> typename std::add_lvalue_reference::type>::type; RESULT_WARN_UNUSED constexpr auto value() const && -> typename std::add_rvalue_reference::type>::type; /// \} /// \{ /// \brief Returns the contained error, if one exists, or a /// default-constructed error value /// /// The `error()` function will not throw any exceptions if `E` does not /// throw any exceptions for the copy or move construction. /// /// This is to limit the possible scope for exceptions, and to allow the /// error type to be treated as a "status"-like type, where the /// default-constructed case is considered the "good" state. /// /// If this function is invoked on an rvalue of a result, the error is /// returned via move-construction /// /// ### Requires /// /// * `std::is_default_constructible::value` is `true` /// * `std::is_copy_constructible::value` or /// `std::is_move_constructible::value` is `true` /// * `E{}` represents the "good" (non-error) state /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ 42 }; /// assert(r.error() == std::error_code{}); /// /// auto r = cpp::result{ /// cpp::fail(std::io_errc::stream) /// }; /// /// assert(r.error() == std::io_errc::stream); /// ``` /// /// \return the error or a default-constructed error value RESULT_WARN_UNUSED constexpr auto error() const & noexcept(std::is_nothrow_constructible::value && std::is_nothrow_copy_constructible::value) -> E; RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto error() && noexcept(std::is_nothrow_constructible::value && std::is_nothrow_move_constructible::value) -> E; /// } /// \{ /// \brief Asserts an expectation that this result contains an error, /// throwing a bad_result_access on failure /// /// If this function is invoked from an rvalue of `result`, then this will /// consume the underlying error first, if there is one. /// /// \note This function exists as a means to allow for results to be marked /// `used` without requiring directly inspecting the underlying value. /// This is, in effect, equivalent to `assert(res.has_value())`, /// however it uses exceptions to ensure the stack can be unwound, and /// exceptions invoked. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto start_service() -> cpp::result; /// /// start_service().expect("Service failed to start!"); /// ``` /// /// \param message the message to provide to this expectation /// /// \return the value of `*this` template ::value && std::is_copy_constructible::value )>::type> RESULT_CPP14_CONSTEXPR auto expect(String&& message) & -> typename std::add_lvalue_reference::type; template ::value && std::is_move_constructible::value )>::type> RESULT_CPP14_CONSTEXPR auto expect(String&& message) && -> typename std::add_rvalue_reference::type; template ::value && std::is_copy_constructible::value )>::type> RESULT_CPP14_CONSTEXPR auto expect(String&& message) const & -> typename std::add_lvalue_reference::type>::type; template ::value && std::is_copy_constructible::value )>::type> RESULT_CPP14_CONSTEXPR auto expect(String&& message) const && -> typename std::add_rvalue_reference::type>::type; /// \} //------------------------------------------------------------------------- // Monadic Functionalities //------------------------------------------------------------------------- public: /// \{ /// \brief Returns the contained value if `*this` has a value, /// otherwise returns \p default_value. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{42}; /// assert(r.value_or(0) == 42); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.value_or(0) == 0); /// ``` /// /// \param default_value the value to use in case `*this` contains an error /// \return the contained value or \p default_value template RESULT_WARN_UNUSED constexpr auto value_or(U&& default_value) const & -> typename std::remove_reference::type; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto value_or(U&& default_value) && -> typename std::remove_reference::type; /// \} /// \{ /// \brief Returns the contained error if `*this` has an error, /// otherwise returns \p default_error. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{42}; /// assert(r.error_or(0) == cpp::fail(0)); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.error_or(0) == cpp::fail(42)); /// ``` /// /// \param default_error the error to use in case `*this` is empty /// \return the contained value or \p default_error template RESULT_WARN_UNUSED constexpr auto error_or(U&& default_error) const & -> error_type; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto error_or(U&& default_error) && -> error_type; /// \} //------------------------------------------------------------------------- /// \brief Returns a result containing \p value if this result contains /// a value, otherwise returns a result containing the current /// error. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{42}; /// assert(r.and_then(100) == 100); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.and_then(100) == cpp::fail(42)); /// ``` /// /// \param value the value to return as a result /// \return a result of \p value if this contains a value template RESULT_WARN_UNUSED constexpr auto and_then(U&& value) const -> result::type,E>; /// \{ /// \brief Invokes the function \p fn with the value of this result as /// the argument /// /// If this result contains an error, a result of the error is returned /// /// The function being called must return a `result` type or the program /// is ill-formed /// /// If this is called on an rvalue of `result` which contains an error, /// the returned `result` is constructed from an rvalue of that error. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto to_string(int) -> cpp::result; /// auto r = cpp::result{42}; /// assert(r.flat_map(to_string) == "42"); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.flat_map(to_string) == cpp::fail(42)); /// ``` /// /// \param fn the function to invoke with this /// \return The result of the function being called template RESULT_WARN_UNUSED constexpr auto flat_map(Fn&& fn) const & -> detail::invoke_result_t; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map(Fn&& fn) && -> detail::invoke_result_t; /// \} /// \{ /// \brief Invokes the function \p fn with the value of this result as /// the argument /// /// If this result is an error, the result of this function is that /// error. Otherwise this function wraps the result and returns it as an /// result. /// /// If this is called on an rvalue of `result` which contains an error, /// the returned `result` is constructed from an rvalue of that error. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto to_string(int) -> std::string; /// auto r = cpp::result{42}; /// assert(r.map(to_string) == "42"); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.map(to_string) == cpp::fail(42)); /// ``` /// /// \param fn the function to invoke with this /// \return The result result of the function invoked template RESULT_WARN_UNUSED constexpr auto map(Fn&& fn) const & -> result,E>; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto map(Fn&& fn) && -> result,E>; /// \} /// \{ /// \brief Invokes the function \p fn with the error of this result as /// the argument /// /// If this result contains a value, the result of this function is that /// value. Otherwise the function is called with that error and returns the /// result as a result. /// /// If this is called on an rvalue of `result` which contains a value, /// the returned `result` is constructed from an rvalue of that value. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto to_string(int) -> std::string; /// auto r = cpp::result{42}; /// assert(r.map_error(to_string) == 42); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.map_error(to_string) == cpp::fail("42")); /// /// auto r = cpp::result{}; /// auto s = r.map(std::string::size); // 's' contains 'result' /// ``` /// /// \param fn the function to invoke with this /// \return The result result of the function invoked template RESULT_WARN_UNUSED constexpr auto map_error(Fn&& fn) const & -> result>; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto map_error(Fn&& fn) && -> result>; /// \} /// \{ /// \brief Invokes the function \p fn with the error of this result as /// the argument /// /// If this result contains a value, a result of the value is returned /// /// The function being called must return a `result` type or the program /// is ill-formed /// /// If this is called on an rvalue of `result` which contains an error, /// the returned `result` is constructed from an rvalue of that error. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto to_string(int) -> cpp::result; /// auto r = cpp::result{42}; /// assert(r.flat_map(to_string) == 42); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.flat_map(to_string) == cpp::fail("42")); /// ``` /// /// \param fn the function to invoke with this /// \return The result of the function being called template RESULT_WARN_UNUSED constexpr auto flat_map_error(Fn&& fn) const & -> detail::invoke_result_t; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn&& fn) && -> detail::invoke_result_t; /// \} //------------------------------------------------------------------------- // Private Members //------------------------------------------------------------------------- private: detail::result_storage m_storage; //------------------------------------------------------------------------- // Private Monadic Functions //------------------------------------------------------------------------- private: /// \{ /// \brief Map implementations for void and non-void functions /// /// \param fn the function template constexpr auto map_impl(std::true_type, Fn&& fn) const & -> result; template constexpr auto map_impl(std::false_type, Fn&& fn) const & -> result,E>; template RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn&& fn) && -> result; template RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn&& fn) && -> result,E>; /// \} }; //=========================================================================== // class : result //=========================================================================== ///////////////////////////////////////////////////////////////////////////// /// \brief Partial specialization of `result` /// /// \tparam E the underlying error type ///////////////////////////////////////////////////////////////////////////// template class RESULT_NODISCARD result { // Type requirements static_assert( !std::is_void::type>::value, "It is ill-formed for E to be (possibly CV-qualified) void type" ); static_assert( !std::is_abstract::value, "It is ill-formed for E to be abstract type" ); static_assert( !is_failure::type>::value, "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type" ); static_assert( !std::is_reference::value, "It is ill-formed for E to be a reference type. " "Only T types may be lvalue references" ); // Friendship friend detail::result_error_extractor; template friend class result; //------------------------------------------------------------------------- // Public Member Types //------------------------------------------------------------------------- public: using value_type = void; ///< The value type of this result using error_type = E; ///< The error type of this result using failure_type = failure; ///< The failure type template using rebind = result; ///< Rebinds the result type //------------------------------------------------------------------------- // Constructor / Assignment //------------------------------------------------------------------------- public: /// \brief Constructs a `result` object in a value state /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{}; /// ``` constexpr result() noexcept; /// \brief Copy constructs this result /// /// If other contains an error, constructs an object that contains a copy /// of that error. /// /// \note This constructor is defined as deleted if /// `std::is_copy_constructible::value` is `false` /// /// \note This constructor is defined as trivial if both /// `std::is_trivially_copy_constructible::value` are `true` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// const auto r = cpp::result{}; /// const auto s = r; /// ``` /// /// \param other the result to copy constexpr result(const result& other) = default; /// \brief Move constructs a result /// /// If other contains an error, move-constructs this result from that /// error. /// /// \note This constructor is defined as deleted if /// `std::is_move_constructible::value` is `false` /// /// \note This constructor is defined as trivial if both /// `std::is_trivially_move_constructible::value` are `true` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{}; /// auto s = std::move(r); /// ``` /// /// \param other the result to move constexpr result(result&& other) = default; /// \brief Converting copy constructor /// /// If \p other contains a value, constructs a result object that is not /// in an error state -- ignoring the value. /// /// If \p other contains an error, constructs a result object that /// contains an error, initialized as if direct-initializing /// (but not direct-list-initializing) an object of type `E`. /// /// \note This constructor does not participate in overload resolution /// unless the following conditions are met: /// - `std::is_constructible_v` is `true` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// const auto r = cpp::result{42}; /// const auto s = cpp::result{r}; /// ``` /// /// \param other the other type to convert template ::value>::type> explicit result(const result& other) noexcept(std::is_nothrow_constructible::value); /// \brief Converting move constructor /// /// If \p other contains an error, constructs a result object that /// contains an error, initialized as if direct-initializing /// (but not direct-list-initializing) an object of type E&&. /// /// \note This constructor does not participate in overload resolution /// unless the following conditions are met: /// - `std::is_constructible_v` is `true` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{42}; /// auto s = cpp::result{ /// std::move(r) /// }; /// ``` /// /// \param other the other type to convert template ::value>::type> explicit result(result&& other) noexcept(std::is_nothrow_constructible::value); //------------------------------------------------------------------------- /// \brief Constructs a result object in a value state /// /// This constructor exists primarily for symmetry with the `result` /// constructor. Unlike the `T` overload, no variadic arguments may be /// supplied. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{cpp::in_place}; /// ``` constexpr explicit result(in_place_t) noexcept; /// \brief Constructs a result object that contains an error /// /// the value is initialized as if direct-initializing (but not /// direct-list-initializing) an object of type `E` from the arguments /// `std::forward(args)...` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// cpp::in_place_error, "Hello world" /// }; /// ``` /// /// \param args the arguments to pass to `E`'s constructor template ::value>::type> constexpr explicit result(in_place_error_t, Args&&... args) noexcept(std::is_nothrow_constructible::value); /// \brief Constructs a result object that contains an error /// /// The value is initialized as if direct-initializing (but not /// direct-list-initializing) an object of type `E` from the arguments /// `std::forward>(ilist)`, /// `std::forward(args)...` /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto r = cpp::result{ /// cpp::in_place_error, {'H','e','l','l','o'} /// }; /// ``` /// /// \param ilist An initializer list of entries to forward /// \param args the arguments to pass to Es constructor template &, Args...>::value>::type> constexpr explicit result(in_place_error_t, std::initializer_list ilist, Args&&...args) noexcept(std::is_nothrow_constructible, Args...>::value); //------------------------------------------------------------------------- /// \{ /// \brief Constructs the underlying error of this result /// /// \note This constructor only participates in overload resolution if /// `E` is constructible from \p e /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// cpp::result r = cpp::fail(42); /// /// auto get_error_result() -> cpp::result { /// return cpp::fail("hello world!"); /// } /// ``` /// /// \param e the failure error template ::value>::type> constexpr /* implicit */ result(const failure& e) noexcept(std::is_nothrow_constructible::value); template ::value>::type> constexpr /* implicit */ result(failure&& e) noexcept(std::is_nothrow_constructible::value); /// \} //------------------------------------------------------------------------- /// \brief Copy assigns the result stored in \p other /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_copy_constructible_v` is `true` /// this restriction guarantees that no 'valueless_by_exception` state /// may occur. /// /// \note This assignment operator is defined as trivial if the following /// conditions are all `true`: /// - `std::is_trivially_copy_constructible::value` /// - `std::is_trivially_copy_assignable::value` /// - `std::is_trivially_destructible::value` /// /// \param other the other result to copy auto operator=(const result& other) -> result& = default; /// \brief Move assigns the result stored in \p other /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_copy_constructible_v` is `true` /// this restriction guarantees that no 'valueless_by_exception` state /// may occur. /// /// \note This assignment operator is defined as trivial if the following /// conditions are all `true`: /// - `std::is_trivially_move_constructible::value` /// - `std::is_trivially_move_assignable::value` /// - `std::is_trivially_destructible::value` /// /// \param other the other result to copy auto operator=(result&& other) -> result& = default; /// \brief Copy-converts the state of \p other /// /// If both this and \p other contain an error, the underlying error is /// assigned through copy-assignment. /// /// If \p other contains a value state, this result is constructed in a /// value state. /// /// If \p other contans an error state, and this contains a value state, /// the underlying error is constructed through copy-construction. /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_constructible_v`, /// `std::is_assignable_v` are all `true`. /// /// \param other the other result object to convert /// \return reference to `(*this)` template ::value && std::is_assignable::value>::type> auto operator=(const result& other) noexcept(std::is_nothrow_assignable::value) -> result&; /// \brief Move-converts the state of \p other /// /// If both this and \p other contain an error, the underlying error is /// assigned through move-assignment. /// /// If \p other contains a value state, this result is constructed in a /// value state. /// /// If \p other contans an error state, and this contains a value state, /// the underlying error is constructed through move-construction. /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_constructible_v`, /// `std::is_assignable_v` are all `true`. /// /// \param other the other result object to convert /// \return reference to `(*this)` template ::value && std::is_assignable::value>::type> auto operator=(result&& other) noexcept(std::is_nothrow_assignable::value) -> result&; /// \{ /// \brief Perfect-forwarded assignment /// /// Depending on whether `*this` contains an error before the call, the /// contained error is either direct-initialized via forwarding the error, /// or assigned from forwarding the error /// /// \note The function does not participate in overload resolution unless /// - `std::is_nothrow_constructible_v` is `true`, and /// - `std::is_assignable_v` is `true` /// /// \param other the failure value to assign to this /// \return reference to `(*this)` template ::value>::type> auto operator=(const failure& other) noexcept(std::is_nothrow_assignable::value) -> result&; template ::value>::type> auto operator=(failure&& other) noexcept(std::is_nothrow_assignable::value) -> result&; /// \} //------------------------------------------------------------------------- // Observers //------------------------------------------------------------------------- public: /// \brief Contextually convertible to `true` if `*this` does not contain /// an error /// /// This function exists to allow for simple, terse checks for containing /// a value. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto get_result() -> cpp::result; /// auto r = get_result(); /// if (r) { ... } /// /// assert(static_cast(cpp::result{})); /// /// assert(!static_cast(cpp::result{cpp::fail(42)})); /// ``` /// /// \return `true` if `*this` contains a value, `false` if `*this` /// does not contain a value RESULT_WARN_UNUSED constexpr explicit operator bool() const noexcept; /// \copydoc result::has_value RESULT_WARN_UNUSED constexpr auto has_value() const noexcept -> bool; /// \copydoc result::has_error RESULT_WARN_UNUSED constexpr auto has_error() const noexcept -> bool; //------------------------------------------------------------------------- /// \{ /// \brief Throws an exception if `(*this)` is in an error state /// /// This function exists for symmetry with `cpp::result` objects where /// `T` contains a value. /// /// If this contains an error, an exception is thrown containing the /// underlying error. The error is consumed propagating the same constness /// and refness of this result. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// cpp::result{}.value(); // no exception /// /// auto r = cpp::result>{ /// cpp::fail(std::make_unique(42)) /// }; /// std::move(r).value(); // throws bad_result_access> /// /// try { /// auto r = cpp::result{ cpp::fail(42) }.value(); /// } catch (const cpp::bad_result_access& e) { /// assert(e.error() == 42); /// } /// ``` /// /// \throws bad_result_access if `*this` does not contain a value. RESULT_CPP14_CONSTEXPR auto value() && -> void; RESULT_CPP14_CONSTEXPR auto value() const & -> void; /// \} /// \{ /// \copydoc result::error RESULT_WARN_UNUSED constexpr auto error() const & noexcept(std::is_nothrow_constructible::value && std::is_nothrow_copy_constructible::value) -> E; RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto error() && noexcept(std::is_nothrow_constructible::value && std::is_nothrow_copy_constructible::value) -> E; /// \} /// \{ /// \copydoc result::expect template ::value && std::is_copy_constructible::value )>::type> RESULT_CPP14_CONSTEXPR auto expect(String&& message) const & -> void; template ::value && std::is_move_constructible::value )>::type> RESULT_CPP14_CONSTEXPR auto expect(String&& message) && -> void; /// \} //------------------------------------------------------------------------- // Monadic Functionalities //------------------------------------------------------------------------- public: /// \{ /// \copydoc result::error_or template RESULT_WARN_UNUSED constexpr auto error_or(U&& default_error) const & -> error_type; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto error_or(U&& default_error) && -> error_type; /// \} //------------------------------------------------------------------------- /// \copydoc result::and_then template RESULT_WARN_UNUSED constexpr auto and_then(U&& value) const -> result::type,E>; /// \{ /// \brief Invokes the function \p fn if `(*this)` contains no value /// /// If this result contains an error, a result of the error is returned /// /// The function being called must return a `result` type or the program /// is ill-formed /// /// If this is called on an rvalue of `result` which contains an error, /// the returned `result` is constructed from an rvalue of that error. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto generate_int() -> cpp::result { return 42; } /// auto r = cpp::result{}; /// assert(r.flat_map(generate_int) == 42); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.flat_map(generate_int) == cpp::fail(42)); /// ``` /// /// \param fn the function to invoke with this /// \return The result of the function being called template RESULT_WARN_UNUSED constexpr auto flat_map(Fn&& fn) const & -> detail::invoke_result_t; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map(Fn&& fn) && -> detail::invoke_result_t; /// \} /// \{ /// \brief Invokes the function \p fn if `(*this)` contains no value /// /// If this result is an error, the result of this function is that /// error. Otherwise this function wraps the result and returns it as an /// result. /// /// If this is called on an rvalue of `result` which contains an error, /// the returned `result` is constructed from an rvalue of that error. /// /// ### Examples /// /// Basic Usage: /// /// ```cpp /// auto generate_int() -> int { return 42; } /// auto r = cpp::result{}; /// assert(r.map(generate_int) == 42); /// /// auto r = cpp::result{cpp::fail(42)}; /// assert(r.map(generate_int) == cpp::fail(42)); /// ``` /// /// \param fn the function to invoke with this /// \return The result result of the function invoked template RESULT_WARN_UNUSED constexpr auto map(Fn&& fn) const & -> result,E>; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto map(Fn&& fn) && -> result,E>; /// \} /// \{ /// \copydoc result::map_error template constexpr auto map_error(Fn&& fn) const & -> result>; template RESULT_CPP14_CONSTEXPR auto map_error(Fn&& fn) && -> result>; /// \} /// \{ /// \copydoc result::flat_map_error template RESULT_WARN_UNUSED constexpr auto flat_map_error(Fn&& fn) const & -> detail::invoke_result_t; template RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn&& fn) && -> detail::invoke_result_t; /// \} //------------------------------------------------------------------------- // Private Members //------------------------------------------------------------------------- private: detail::result_storage m_storage; //------------------------------------------------------------------------- // Private Monadic Functions //------------------------------------------------------------------------- private: /// \{ /// \brief Map implementations for void and non-void functions /// /// \param fn the function template constexpr auto map_impl(std::true_type, Fn&& fn) const & -> result; template constexpr auto map_impl(std::false_type, Fn&& fn) const & -> result,E>; template RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn&& fn) && -> result; template RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn&& fn) && -> result,E>; /// \} }; //=========================================================================== // non-member functions : class : result //=========================================================================== //--------------------------------------------------------------------------- // Comparison //--------------------------------------------------------------------------- template constexpr auto operator==(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator!=(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator>=(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator<=(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator>(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator<(const result& lhs, const result& rhs) noexcept -> bool; //--------------------------------------------------------------------------- template constexpr auto operator==(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator!=(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator>=(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator<=(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator>(const result& lhs, const result& rhs) noexcept -> bool; template constexpr auto operator<(const result& lhs, const result& rhs) noexcept -> bool; //--------------------------------------------------------------------------- template ::value>::type> constexpr auto operator==(const result& exp, const U& value) noexcept -> bool; template ::value>::type> constexpr auto operator==(const T& value, const result& exp) noexcept -> bool; template ::value>::type> constexpr auto operator!=(const result& exp, const U& value) noexcept -> bool; template ::value>::type> constexpr auto operator!=(const T& value, const result& exp) noexcept -> bool; template ::value>::type> constexpr auto operator<=(const result& exp, const U& value) noexcept -> bool; template ::value>::type> constexpr auto operator<=(const T& value, const result& exp) noexcept -> bool; template ::value>::type> constexpr auto operator>=(const result& exp, const U& value) noexcept -> bool; template ::value>::type> constexpr auto operator>=(const T& value, const result& exp) noexcept -> bool; template ::value>::type> constexpr auto operator<(const result& exp, const U& value) noexcept -> bool; template ::value>::type> constexpr auto operator<(const T& value, const result& exp) noexcept -> bool; template ::value>::type> constexpr auto operator>(const result& exp, const U& value) noexcept -> bool; template ::value>::type> constexpr auto operator>(const T& value, const result& exp) noexcept -> bool; //--------------------------------------------------------------------------- template constexpr auto operator==(const result& exp, const failure& value) noexcept -> bool; template constexpr auto operator==(const failure& value, const result& exp) noexcept -> bool; template constexpr auto operator!=(const result& exp, const failure& value) noexcept -> bool; template constexpr auto operator!=(const failure& value, const result& exp) noexcept -> bool; template constexpr auto operator<=(const result& exp, const failure& value) noexcept -> bool; template constexpr auto operator<=(const failure& value, const result& exp) noexcept -> bool; template constexpr auto operator>=(const result& exp, const failure& value) noexcept -> bool; template constexpr auto operator>=(const failure& value, const result& exp) noexcept -> bool; template constexpr auto operator<(const result& exp, const failure& value) noexcept -> bool; template constexpr auto operator<(const failure& value, const result& exp) noexcept -> bool; template constexpr auto operator>(const result& exp, const failure& value) noexcept -> bool; template constexpr auto operator>(const failure& value, const result& exp) noexcept -> bool; //--------------------------------------------------------------------------- // Utilities //--------------------------------------------------------------------------- /// \{ /// \brief Swaps the contents of \p lhs with \p rhs /// /// \param lhs the left result /// \param rhs the right result template auto swap(result& lhs, result& rhs) #if __cplusplus >= 201703L noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value && std::is_nothrow_swappable::value && std::is_nothrow_swappable::value) #else noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value) #endif -> void; template auto swap(result& lhs, result& rhs) #if __cplusplus >= 201703L noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value && std::is_nothrow_swappable::value) #else noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value) #endif -> void; /// \} } // inline namespace bitwizeshift } // namespace EXPECTED_NAMESPACE namespace std { template struct hash<::RESULT_NS_IMPL::result> { auto operator()(const RESULT_NS_IMPL::result& x) const -> std::size_t { if (x.has_value()) { return std::hash{}(*x) + 1; // add '1' to differentiate from error case } return std::hash{}(::RESULT_NS_IMPL::detail::extract_error(x)); } }; template struct hash<::RESULT_NS_IMPL::result> { auto operator()(const RESULT_NS_IMPL::result& x) const -> std::size_t { if (x.has_value()) { return 0; } return std::hash{}(::RESULT_NS_IMPL::detail::extract_error(x)); } }; } // namespace std #if !defined(RESULT_DISABLE_EXCEPTIONS) //============================================================================= // class : bad_result_access //============================================================================= //----------------------------------------------------------------------------- // Constructors //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::bad_result_access::bad_result_access(E2&& error) : logic_error{"error attempting to access value from result containing error"}, m_error(detail::forward(error)) { } template template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::bad_result_access::bad_result_access( const char* what_arg, E2&& error ) : logic_error{what_arg}, m_error(detail::forward(error)) { } template template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::bad_result_access::bad_result_access( const std::string& what_arg, E2&& error ) : logic_error{what_arg}, m_error(detail::forward(error)) { } //----------------------------------------------------------------------------- // Observers //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::bad_result_access::error() & noexcept -> E& { return m_error; } template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::bad_result_access::error() && noexcept -> E&& { return static_cast(m_error); } template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::bad_result_access::error() const & noexcept -> const E& { return m_error; } template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::bad_result_access::error() const && noexcept -> const E&& { return static_cast(m_error); } #endif //============================================================================= // class : failure //============================================================================= //----------------------------------------------------------------------------- // Constructors //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure::failure(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_failure(detail::forward(args)...) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure::failure( in_place_t, std::initializer_list ilist, Args&&...args ) noexcept(std::is_nothrow_constructible, Args...>::value) : m_failure(ilist, detail::forward(args)...) { } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure::failure(E2&& error) noexcept(std::is_nothrow_constructible::value) : m_failure(detail::forward(error)) { } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure::failure(E2&& error) noexcept(std::is_nothrow_constructible::value) : m_failure(detail::forward(error)) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure::failure(const failure& other) noexcept(std::is_nothrow_constructible::value) : m_failure(other.error()) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure::failure(failure&& other) noexcept(std::is_nothrow_constructible::value) : m_failure(static_cast&&>(other).error()) { } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::failure::operator=(E2&& error) noexcept( std::is_nothrow_assignable::value || std::is_lvalue_reference::value ) -> failure& { m_failure = detail::forward(error); return (*this); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::failure::operator=(const failure& other) noexcept(std::is_nothrow_assignable::value) -> failure& { m_failure = other.error(); return (*this); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::failure::operator=(failure&& other) noexcept(std::is_nothrow_assignable::value) -> failure& { m_failure = static_cast&&>(other).error(); return (*this); } //----------------------------------------------------------------------------- // Observers //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::failure::error() & noexcept -> typename std::add_lvalue_reference::type { return m_failure; } template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::failure::error() && noexcept -> typename std::add_rvalue_reference::type { using reference = typename std::add_rvalue_reference::type; return static_cast(m_failure); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::failure::error() const & noexcept -> typename std::add_lvalue_reference::type>::type { return m_failure; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::failure::error() const && noexcept -> typename std::add_rvalue_reference::type>::type { using reference = typename std::add_rvalue_reference::type>::type; return static_cast(m_failure); } //============================================================================= // non-member functions : class : failure //============================================================================= //----------------------------------------------------------------------------- // Comparison //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator==(const failure& lhs, const failure& rhs) noexcept -> bool { return lhs.error() == rhs.error(); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator!=(const failure& lhs, const failure& rhs) noexcept -> bool { return lhs.error() != rhs.error(); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<(const failure& lhs, const failure& rhs) noexcept -> bool { return lhs.error() < rhs.error(); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>(const failure& lhs, const failure& rhs) noexcept -> bool { return lhs.error() > rhs.error(); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<=(const failure& lhs, const failure& rhs) noexcept -> bool { return lhs.error() <= rhs.error(); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>=(const failure& lhs, const failure& rhs) noexcept -> bool { return lhs.error() >= rhs.error(); } //----------------------------------------------------------------------------- // Utilities //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::fail(E&& e) noexcept(std::is_nothrow_constructible::type,E>::value) -> failure::type> { using result_type = failure::type>; return result_type( detail::forward(e) ); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::fail(std::reference_wrapper e) noexcept -> failure { using result_type = failure; return result_type{e.get()}; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::fail(Args&&...args) noexcept(std::is_nothrow_constructible::value) -> failure { return failure(in_place, detail::forward(args)...); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::fail(std::initializer_list ilist, Args&&...args) noexcept(std::is_nothrow_constructible, Args...>::value) -> failure { return failure(in_place, ilist, detail::forward(args)...); } template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::swap(failure& lhs, failure& rhs) #if __cplusplus >= 201703L noexcept(std::is_nothrow_swappable::value) -> void #else noexcept(std::is_nothrow_move_constructible::value) -> void #endif { using std::swap; swap(lhs.error(), rhs.error()); } //============================================================================= // class : detail::result_union //============================================================================= //----------------------------------------------------------------------------- // Constructors / Assignment //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_union ::result_union(unit) noexcept : m_empty{} { // m_has_value intentionally not set } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union ::result_union(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_value(detail::forward(args)...), m_has_value{true} { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union ::result_union(in_place_error_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_error(detail::forward(args)...), m_has_value{false} { } //----------------------------------------------------------------------------- // Modifiers //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_union::destroy() const noexcept -> void { // do nothing } //============================================================================= // class : detail::result_union //============================================================================= //----------------------------------------------------------------------------- // Constructors / Destructor / Assignment //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_union ::result_union(unit) noexcept : m_empty{} { // m_has_value intentionally not set } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union ::result_union(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_value(detail::forward(args)...), m_has_value{true} { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union ::result_union(in_place_error_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_error(detail::forward(args)...), m_has_value{false} { } //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_union ::~result_union() noexcept(std::is_nothrow_destructible::value && std::is_nothrow_destructible::value) { destroy(); } //----------------------------------------------------------------------------- // Modifiers //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_union::destroy() -> void { if (m_has_value) { m_value.~underlying_value_type(); } else { m_error.~underlying_error_type(); } } //============================================================================= // class : result_construct_base //============================================================================= //----------------------------------------------------------------------------- // Constructors / Assignment //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_construct_base::result_construct_base(unit) noexcept : storage{unit{}} { } template template inline constexpr RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_construct_base::result_construct_base( in_place_t, Args&&...args ) noexcept(std::is_nothrow_constructible::value) : storage{in_place, detail::forward(args)...} { } template template inline constexpr RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_construct_base::result_construct_base( in_place_error_t, Args&&...args ) noexcept(std::is_nothrow_constructible::value) : storage(in_place_error, detail::forward(args)...) { } //----------------------------------------------------------------------------- // Construction / Assignment //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::construct_value(Args&&...args) noexcept(std::is_nothrow_constructible::value) -> void { using value_type = typename storage_type::underlying_value_type; auto* p = static_cast(std::addressof(storage.m_value)); new (p) value_type(detail::forward(args)...); storage.m_has_value = true; } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::construct_error(Args&&...args) noexcept(std::is_nothrow_constructible::value) -> void { using error_type = typename storage_type::underlying_error_type; auto* p = static_cast(std::addressof(storage.m_error)); new (p) error_type(detail::forward(args)...); storage.m_has_value = false; } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::construct_error_from_result( Result&& other ) -> void { if (other.storage.m_has_value) { construct_value(); } else { construct_error(detail::forward(other).storage.m_error); } } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::construct_from_result( Result&& other ) -> void { if (other.storage.m_has_value) { construct_value_from_result_impl( std::is_lvalue_reference{}, detail::forward(other).storage.m_value ); } else { construct_error(detail::forward(other).storage.m_error); } } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::assign_value(Value&& value) noexcept(std::is_nothrow_assignable::value) -> void { if (!storage.m_has_value) { storage.destroy(); construct_value(detail::forward(value)); } else { storage.m_value = detail::forward(value); } } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::assign_error(Error&& error) noexcept(std::is_nothrow_assignable::value) -> void { if (storage.m_has_value) { storage.destroy(); construct_error(detail::forward(error)); } else { storage.m_error = detail::forward(error); } } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::assign_from_result(Result&& other) -> void { if (other.storage.m_has_value != storage.m_has_value) { storage.destroy(); construct_from_result(detail::forward(other)); } else if (storage.m_has_value) { assign_value_from_result_impl( std::is_lvalue_reference{}, detail::forward(other) ); } else { storage.m_error = detail::forward(other).storage.m_error; } } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::construct_value_from_result_impl( std::true_type, ReferenceWrapper&& reference ) noexcept -> void { using value_type = typename storage_type::underlying_value_type; auto* p = static_cast(std::addressof(storage.m_value)); new (p) value_type(reference.get()); storage.m_has_value = true; } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::construct_value_from_result_impl( std::false_type, Value&& value ) noexcept(std::is_nothrow_constructible::value) -> void { using value_type = typename storage_type::underlying_value_type; auto* p = static_cast(std::addressof(storage.m_value)); new (p) value_type(detail::forward(value)); storage.m_has_value = true; } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::assign_value_from_result_impl( std::true_type, Result&& other ) -> void { // T is a reference; unwrap it storage.m_value = other.storage.m_value.get(); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_construct_base::assign_value_from_result_impl( std::false_type, Result&& other ) -> void { storage.m_value = detail::forward(other).storage.m_value; } //============================================================================= // class : result_trivial_copy_ctor_base_impl //============================================================================= template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_trivial_copy_ctor_base_impl ::result_trivial_copy_ctor_base_impl(const result_trivial_copy_ctor_base_impl& other) noexcept(std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_constructible::value) : base_type(unit{}) { using ctor_base = result_construct_base; ctor_base::construct_from_result(static_cast(other)); } //============================================================================= // class : result_trivial_move_ctor_base //============================================================================= template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_trivial_move_ctor_base_impl ::result_trivial_move_ctor_base_impl(result_trivial_move_ctor_base_impl&& other) noexcept(std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value) : base_type(unit{}) { using ctor_base = result_construct_base; ctor_base::construct_from_result(static_cast(other)); } //============================================================================= // class : result_copy_assign_base //============================================================================= template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_trivial_copy_assign_base_impl ::operator=(const result_trivial_copy_assign_base_impl& other) noexcept(std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_assignable::value) -> result_trivial_copy_assign_base_impl& { using ctor_base = result_construct_base; ctor_base::assign_from_result(static_cast(other)); return (*this); } //========================================================================= // class : result_move_assign_base //========================================================================= template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::result_trivial_move_assign_base_impl ::operator=(result_trivial_move_assign_base_impl&& other) noexcept(std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value && std::is_nothrow_move_assignable::value) -> result_trivial_move_assign_base_impl& { using ctor_base = result_construct_base; ctor_base::assign_from_result(static_cast(other)); return (*this); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::detail::result_error_extractor::get(const result& exp) noexcept -> const E& { return exp.m_storage.storage.m_error; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::detail::result_error_extractor::get(result& exp) noexcept -> E& { return exp.m_storage.storage.m_error; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::detail::extract_error(const result& exp) noexcept -> const E& { return result_error_extractor::get(exp); } template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::throw_bad_result_access(E&& error) -> void { #if defined(RESULT_DISABLE_EXCEPTIONS) std::fprintf( stderr, "error attempting to access value from result containing error\n" ); std::abort(); #else using exception_type = bad_result_access< typename std::remove_const< typename std::remove_reference::type >::type >; throw exception_type{ detail::forward(error) }; #endif } template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::detail::throw_bad_result_access_message( String&& message, E&& error ) -> void { #if defined(RESULT_DISABLE_EXCEPTIONS) const auto message_string = std::string{ detail::forward(message) }; std::fprintf(stderr, "%s\n", message_string.c_str()); std::abort(); #else using exception_type = bad_result_access< typename std::remove_const< typename std::remove_reference::type >::type >; throw exception_type{ detail::forward(message), detail::forward(error) }; #endif } //============================================================================= // class : result //============================================================================= template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result() noexcept(std::is_nothrow_constructible::value) : m_storage(in_place) { } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::result::result(const result& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value) : m_storage(detail::unit{}) { m_storage.construct_from_result( static_cast&>(other).m_storage ); } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::result::result(const result& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value) : m_storage(detail::unit{}) { m_storage.construct_from_result( static_cast&>(other).m_storage ); } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::result::result(result&& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value) : m_storage(detail::unit{}) { m_storage.construct_from_result( static_cast&&>(other).m_storage ); } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::result::result(result&& other) noexcept(std::is_nothrow_constructible::value && std::is_nothrow_constructible::value) : m_storage(detail::unit{}) { m_storage.construct_from_result( static_cast&&>(other).m_storage ); } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(in_place_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place, detail::forward(args)...) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result( in_place_t, std::initializer_list ilist, Args&&...args ) noexcept(std::is_nothrow_constructible, Args...>::value) : m_storage(in_place, ilist, detail::forward(args)...) { } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(in_place_error_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place_error, detail::forward(args)...) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result( in_place_error_t, std::initializer_list ilist, Args&&...args ) noexcept(std::is_nothrow_constructible, Args...>::value) : m_storage(in_place_error, ilist, detail::forward(args)...) { } //------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(const failure& e) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place_error, e.error()) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(failure&& e) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place_error, static_cast(e.error())) { } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(U&& value) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place, detail::forward(value)) { } template template ::value,int>::type> inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(U&& value) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place, detail::forward(value)) { } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(const result& other) noexcept(std::is_nothrow_assignable::value && std::is_nothrow_assignable::value) -> result& { m_storage.assign_from_result( static_cast&>(other).m_storage ); return (*this); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(result&& other) noexcept(std::is_nothrow_assignable::value && std::is_nothrow_assignable::value) -> result& { m_storage.assign_from_result( static_cast&&>(other).m_storage ); return (*this); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(U&& value) noexcept(std::is_nothrow_assignable::value) -> result& { m_storage.assign_value(detail::forward(value)); return (*this); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(const failure& other) noexcept(std::is_nothrow_assignable::value) -> result& { m_storage.assign_error(other.error()); return (*this); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(failure&& other) noexcept(std::is_nothrow_assignable::value) -> result& { m_storage.assign_error(static_cast(other.error())); return (*this); } //----------------------------------------------------------------------------- // Observers //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::operator->() noexcept -> typename std::remove_reference::type* { // Prior to C++17, std::addressof was not `constexpr`. // Since `addressof` fixes a relatively obscure issue where users define a // custom `operator&`, the pre-C++17 implementation has been defined to be // `&(**this)` so that it may exist in constexpr contexts. #if __cplusplus >= 201703L return std::addressof(**this); #else return &(**this); #endif } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::operator->() const noexcept -> typename std::remove_reference::type>::type* { #if __cplusplus >= 201703L return std::addressof(**this); #else return &(**this); #endif } template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::operator*() & noexcept -> typename std::add_lvalue_reference::type { return m_storage.storage.m_value; } template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::operator*() && noexcept -> typename std::add_rvalue_reference::type { using reference = typename std::add_rvalue_reference::type; return static_cast(m_storage.storage.m_value); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::operator*() const& noexcept -> typename std::add_lvalue_reference::type>::type { return m_storage.storage.m_value; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::operator*() const&& noexcept -> typename std::add_rvalue_reference::type>::type { using reference = typename std::add_rvalue_reference::type>::type; return static_cast(m_storage.storage.m_value); } //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::operator bool() const noexcept { return m_storage.storage.m_has_value; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::has_value() const noexcept -> bool { return m_storage.storage.m_has_value; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::has_error() const noexcept -> bool { return !m_storage.storage.m_has_value; } //----------------------------------------------------------------------------- // The `has_value()` expression below is incorrectly identified as an unused // value, which results in the `-Wunused-value` warning. This is suppressed // to prevent false-positives #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-value" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-value" #elif defined(_MSC_VER) // Older MSVC versions incorrectly warn on returning a reference to a temporary. // This has been suppressed # pragma warning(push) # pragma warning(disable:4172) #endif template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::value() & -> typename std::add_lvalue_reference::type { return (has_value() || (detail::throw_bad_result_access(m_storage.storage.m_error), false), m_storage.storage.m_value ); } template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::value() && -> typename std::add_rvalue_reference::type { using reference = typename std::add_rvalue_reference::type; return (has_value() || (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true), static_cast(m_storage.storage.m_value) ); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::value() const & -> typename std::add_lvalue_reference::type>::type { return (has_value() || (detail::throw_bad_result_access(m_storage.storage.m_error), true), m_storage.storage.m_value ); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::value() const && -> typename std::add_rvalue_reference::type>::type { using reference = typename std::add_rvalue_reference::type>::type; return (has_value() || (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true), (static_cast(m_storage.storage.m_value)) ); } #if defined(__clang__) # pragma clang diagnostic pop #elif defined(__GNUC__) # pragma GCC diagnostic pop #elif defined(_MSC_VER) # pragma warning(pop) #endif template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::error() const & noexcept(std::is_nothrow_constructible::value && std::is_nothrow_copy_constructible::value) -> E { static_assert( std::is_default_constructible::value, "E must be default-constructible if 'error()' checks are used. " "This is to allow for default-constructed error states to represent the " "'good' state" ); return m_storage.storage.m_has_value ? E{} : m_storage.storage.m_error; } template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::error() && noexcept(std::is_nothrow_constructible::value && std::is_nothrow_move_constructible::value) -> E { static_assert( std::is_default_constructible::value, "E must be default-constructible if 'error()' checks are used. " "This is to allow for default-constructed error states to represent the " "'good' state" ); return m_storage.storage.m_has_value ? E{} : static_cast(m_storage.storage.m_error); } //----------------------------------------------------------------------------- template template inline RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::expect(String&& message) & -> typename std::add_lvalue_reference::type { return (has_value() || (detail::throw_bad_result_access_message( detail::forward(message), m_storage.storage.m_error ), true), m_storage.storage.m_value ); } template template inline RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::expect(String&& message) && -> typename std::add_rvalue_reference::type { using reference = typename std::add_rvalue_reference::type; return (has_value() || (detail::throw_bad_result_access_message( detail::forward(message), static_cast(m_storage.storage.m_error) ), true), static_cast(m_storage.storage.m_value) ); } template template inline RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::expect(String&& message) const & -> typename std::add_lvalue_reference::type>::type { return (has_value() || (detail::throw_bad_result_access_message( detail::forward(message), m_storage.storage.m_error ), true), m_storage.storage.m_value ); } template template inline RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::expect(String&& message) const && -> typename std::add_rvalue_reference::type>::type { using reference = typename std::add_rvalue_reference::type>::type; return (has_value() || (detail::throw_bad_result_access_message( detail::forward(message), static_cast(m_storage.storage.m_error) ), true), (static_cast(m_storage.storage.m_value)) ); } //----------------------------------------------------------------------------- // Monadic Functionalities //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::value_or(U&& default_value) const& -> typename std::remove_reference::type { return m_storage.storage.m_has_value ? m_storage.storage.m_value : detail::forward(default_value); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::value_or(U&& default_value) && -> typename std::remove_reference::type { return m_storage.storage.m_has_value ? static_cast(**this) : detail::forward(default_value); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::error_or(U&& default_error) const& -> error_type { return m_storage.storage.m_has_value ? detail::forward(default_error) : m_storage.storage.m_error; } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::error_or(U&& default_error) && -> error_type { return m_storage.storage.m_has_value ? detail::forward(default_error) : static_cast(m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::and_then(U&& value) const -> result::type,E> { return map([&value](const T&){ return detail::forward(value); }); } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) const & -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map must return a result type or the program is ill-formed" ); return has_value() ? detail::invoke(detail::forward(fn), m_storage.storage.m_value) : result_type(in_place_error, m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) && -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map must return a result type or the program is ill-formed" ); return has_value() ? detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_value)) : result_type(in_place_error, static_cast(m_storage.storage.m_error)); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map(Fn&& fn) const & -> result,E> { using result_type = detail::invoke_result_t; return map_impl(std::is_void{}, detail::forward(fn)); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map(Fn&& fn) && -> result,E> { using result_type = detail::invoke_result_t; return static_cast&&>(*this).map_impl( std::is_void{}, detail::forward(fn) ); } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map_error(Fn&& fn) const & -> result> { using result_type = result>; return has_error() ? result_type(in_place_error, detail::invoke( detail::forward(fn), m_storage.storage.m_error )) : result_type(in_place, m_storage.storage.m_value); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map_error(Fn&& fn) && -> result> { using result_type = result>; return has_error() ? result_type(in_place_error, detail::invoke( detail::forward(fn), static_cast(m_storage.storage.m_error) )) : result_type(static_cast(m_storage.storage.m_value)); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) const & -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map_error must return a result type or the program is ill-formed" ); return has_value() ? result_type(in_place, m_storage.storage.m_value) : detail::invoke(detail::forward(fn), m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) && -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map_error must return a result type or the program is ill-formed" ); return has_value() ? result_type(in_place, static_cast(m_storage.storage.m_value)) : detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_error)); } //----------------------------------------------------------------------------- // Private Monadic Functions //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) const & -> result { using result_type = result; return has_value() ? (detail::invoke(detail::forward(fn), m_storage.storage.m_value), result_type{}) : result_type(in_place_error, m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) const & -> result,E> { using invoke_result_type = detail::invoke_result_t; using result_type = result; return has_value() ? result_type(in_place, detail::invoke( detail::forward(fn), m_storage.storage.m_value )) : result_type(in_place_error, m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) && -> result { using result_type = result; return has_value() ? (detail::invoke( detail::forward(fn), static_cast(m_storage.storage.m_value) ), result_type{}) : result_type(in_place_error, static_cast(m_storage.storage.m_error)); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) && -> result,E> { using invoke_result_type = detail::invoke_result_t; using result_type = result; return has_value() ? result_type(in_place, detail::invoke( detail::forward(fn), static_cast(m_storage.storage.m_value) )) : result_type(in_place_error, static_cast(m_storage.storage.m_error)); } //============================================================================= // class : result //============================================================================= //----------------------------------------------------------------------------- // Constructor / Assignment //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result() noexcept : m_storage(in_place) { } template template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::result::result(const result& other) noexcept(std::is_nothrow_constructible::value) : m_storage(detail::unit{}) { m_storage.construct_error_from_result( static_cast&>(other).m_storage ); } template template inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::result::result(result&& other) noexcept(std::is_nothrow_constructible::value) : m_storage(detail::unit{}) { m_storage.construct_error_from_result( static_cast&&>(other).m_storage ); } //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(in_place_t) noexcept : m_storage(in_place) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(in_place_error_t, Args&&...args) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place_error, detail::forward(args)...) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(in_place_error_t, std::initializer_list ilist, Args&&...args) noexcept(std::is_nothrow_constructible, Args...>::value) : m_storage(in_place_error, ilist, detail::forward(args)...) { } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(const failure& e) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place_error, e.error()) { } template template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::result(failure&& e) noexcept(std::is_nothrow_constructible::value) : m_storage(in_place_error, static_cast(e.error())) { } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(const result& other) noexcept(std::is_nothrow_assignable::value) -> result& { m_storage.assign_from_result(other.m_storage); return (*this); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(result&& other) noexcept(std::is_nothrow_assignable::value) -> result& { m_storage.assign_from_result(static_cast&&>(other).m_storage); return (*this); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(const failure& other) noexcept(std::is_nothrow_assignable::value) -> result& { m_storage.assign_error(other.error()); return (*this); } template template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::result::operator=(failure&& other) noexcept(std::is_nothrow_assignable::value) -> result& { m_storage.assign_error(static_cast(other.error())); return (*this); } //----------------------------------------------------------------------------- // Observers //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result::operator bool() const noexcept { return has_value(); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::has_value() const noexcept -> bool { return m_storage.storage.m_has_value; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::has_error() const noexcept -> bool { return !has_value(); } //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::value() const & -> void { static_cast( has_value() || (detail::throw_bad_result_access(m_storage.storage.m_error), true) ); } template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::value() && -> void { static_cast( has_value() || (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true) ); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::error() const & noexcept(std::is_nothrow_constructible::value && std::is_nothrow_copy_constructible::value) -> E { return has_value() ? E{} : m_storage.storage.m_error; } template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::error() && noexcept(std::is_nothrow_constructible::value && std::is_nothrow_copy_constructible::value) -> E { return has_value() ? E{} : static_cast(m_storage.storage.m_error); } //----------------------------------------------------------------------------- template template inline RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::expect(String&& message) const & -> void { if (has_error()) { detail::throw_bad_result_access_message( detail::forward(message), m_storage.storage.m_error ); } } template template inline RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::expect(String&& message) && -> void { if (has_error()) { detail::throw_bad_result_access_message( detail::forward(message), static_cast(m_storage.storage.m_error) ); } } //----------------------------------------------------------------------------- // Monadic Functionalities //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::error_or(U&& default_error) const & -> error_type { return has_value() ? detail::forward(default_error) : m_storage.storage.m_error; } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::error_or(U&& default_error) && -> error_type { return has_value() ? detail::forward(default_error) : static_cast(m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::and_then(U&& value) const -> result::type,E> { return map([&value]{ return detail::forward(value); }); } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) const & -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map must return a result type or the program is ill-formed" ); return has_value() ? detail::invoke(detail::forward(fn)) : result_type(in_place_error, m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::flat_map(Fn&& fn) && -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map must return a result type or the program is ill-formed" ); return has_value() ? detail::invoke(detail::forward(fn)) : result_type(in_place_error, static_cast(m_storage.storage.m_error)); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map(Fn&& fn) const & -> result,E> { using result_type = detail::invoke_result_t; return map_impl(std::is_void{}, detail::forward(fn)); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map(Fn&& fn) && -> result,E> { using result_type = detail::invoke_result_t; return static_cast&&>(*this).map_impl( std::is_void{}, detail::forward(fn) ); } //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map_error(Fn&& fn) const & -> result> { using result_type = result>; return has_value() ? result_type{} : result_type(in_place_error, detail::invoke( detail::forward(fn), m_storage.storage.m_error )); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map_error(Fn&& fn) && -> result> { using result_type = result>; return has_value() ? result_type{} : result_type(in_place_error, detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_error) )); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) const & -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map_error must return a result type or the program is ill-formed" ); static_assert( std::is_default_constructible::value, "flat_map_error for result requires the new T type to be default-" "constructible" ); return has_value() ? result_type{} : detail::invoke(detail::forward(fn), m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::flat_map_error(Fn&& fn) && -> detail::invoke_result_t { using result_type = detail::invoke_result_t; static_assert( is_result::value, "flat_map_error must return a result type or the program is ill-formed" ); static_assert( std::is_default_constructible::value, "flat_map_error for result requires the new T type to be default-" "constructible" ); return has_value() ? result_type{} : detail::invoke(detail::forward(fn), static_cast(m_storage.storage.m_error)); } //----------------------------------------------------------------------------- // Private Monadic Functions //----------------------------------------------------------------------------- template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) const & -> result { using result_type = result; return has_value() ? (detail::invoke(detail::forward(fn)), result_type{}) : result_type(in_place_error, m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) const & -> result,E> { using invoke_result_type = detail::invoke_result_t; using result_type = result; return has_value() ? result_type(in_place, detail::invoke(detail::forward(fn))) : result_type(in_place_error, m_storage.storage.m_error); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map_impl(std::true_type, Fn&& fn) && -> result { using result_type = result; return has_value() ? (detail::invoke(detail::forward(fn)), result_type{}) : result_type(in_place_error, static_cast(m_storage.storage.m_error)); } template template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::map_impl(std::false_type, Fn&& fn) && -> result,E> { using invoke_result_type = detail::invoke_result_t; using result_type = result; return has_value() ? result_type(in_place, detail::invoke(detail::forward(fn))) : result_type(in_place_error, static_cast(m_storage.storage.m_error)); } //============================================================================= // non-member functions : class : result //============================================================================= //----------------------------------------------------------------------------- // Comparison //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator==(const result& lhs, const result& rhs) noexcept -> bool { return (lhs.has_value() == rhs.has_value()) ? ( lhs.has_value() ? *lhs == *rhs : detail::extract_error(lhs) == detail::extract_error(rhs) ) : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator!=(const result& lhs, const result& rhs) noexcept -> bool { return (lhs.has_value() == rhs.has_value()) ? ( lhs.has_value() ? *lhs != *rhs : detail::extract_error(lhs) != detail::extract_error(rhs) ) : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>=(const result& lhs, const result& rhs) noexcept -> bool { return (lhs.has_value() == rhs.has_value()) ? ( lhs.has_value() ? *lhs >= *rhs : detail::extract_error(lhs) >= detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) >= static_cast(static_cast(rhs)); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<=(const result& lhs, const result& rhs) noexcept -> bool { return (lhs.has_value() == rhs.has_value()) ? ( lhs.has_value() ? *lhs <= *rhs : detail::extract_error(lhs) <= detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) <= static_cast(static_cast(rhs)); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>(const result& lhs, const result& rhs) noexcept -> bool { return (lhs.has_value() == rhs.has_value()) ? ( lhs.has_value() ? *lhs > *rhs : detail::extract_error(lhs) > detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) > static_cast(static_cast(rhs)); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<(const result& lhs, const result& rhs) noexcept -> bool { return (lhs.has_value() == rhs.has_value()) ? ( lhs.has_value() ? *lhs < *rhs : detail::extract_error(lhs) < detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) < static_cast(static_cast(rhs)); } //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator==(const result& lhs, const result& rhs) noexcept -> bool { return lhs.has_value() == rhs.has_value() ? ( lhs.has_value() ? true : detail::extract_error(lhs) == detail::extract_error(rhs) ) : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator!=(const result& lhs, const result& rhs) noexcept -> bool { return lhs.has_value() == rhs.has_value() ? ( lhs.has_value() ? false : detail::extract_error(lhs) != detail::extract_error(rhs) ) : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>=(const result& lhs, const result& rhs) noexcept -> bool { return lhs.has_value() == rhs.has_value() ? ( lhs.has_value() ? true : detail::extract_error(lhs) >= detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) >= static_cast(static_cast(rhs)); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<=(const result& lhs, const result& rhs) noexcept -> bool { return lhs.has_value() == rhs.has_value() ? ( lhs.has_value() ? true : detail::extract_error(lhs) <= detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) <= static_cast(static_cast(rhs)); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>(const result& lhs, const result& rhs) noexcept -> bool { return lhs.has_value() == rhs.has_value() ? ( lhs.has_value() ? false : detail::extract_error(lhs) > detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) > static_cast(static_cast(rhs)); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<(const result& lhs, const result& rhs) noexcept -> bool { return lhs.has_value() == rhs.has_value() ? ( lhs.has_value() ? false : detail::extract_error(lhs) < detail::extract_error(rhs) ) : static_cast(static_cast(lhs)) < static_cast(static_cast(rhs)); } //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator==(const result& exp, const U& value) noexcept -> bool { return (exp.has_value() && *exp == value); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator==(const T& value, const result& exp) noexcept -> bool { return (exp.has_value() && *exp == value); } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator!=(const result& exp, const U& value) noexcept -> bool { return exp.has_value() ? *exp != value : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator!=(const T& value, const result& exp) noexcept -> bool { return exp.has_value() ? value != *exp : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<=(const result& exp, const U& value) noexcept -> bool { return exp.has_value() ? *exp <= value : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<=(const T& value, const result& exp) noexcept -> bool { return exp.has_value() ? value <= *exp : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>=(const result& exp, const U& value) noexcept -> bool { return exp.has_value() ? *exp >= value : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>=(const T& value, const result& exp) noexcept -> bool { return exp.has_value() ? value >= *exp : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<(const result& exp, const U& value) noexcept -> bool { return exp.has_value() ? *exp < value : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<(const T& value, const result& exp) noexcept -> bool { return exp.has_value() ? value < *exp : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>(const result& exp, const U& value) noexcept -> bool { return exp.has_value() ? *exp > value : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>(const T& value, const result& exp) noexcept -> bool { return exp.has_value() ? value > *exp : true; } //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator==(const result& exp, const failure& error) noexcept -> bool { return exp.has_error() ? detail::extract_error(exp) == error.error() : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator==(const failure& error, const result& exp) noexcept -> bool { return exp.has_error() ? error.error() == detail::extract_error(exp) : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator!=(const result& exp, const failure& error) noexcept -> bool { return exp.has_error() ? detail::extract_error(exp) != error.error() : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator!=(const failure& error, const result& exp) noexcept -> bool { return exp.has_error() ? error.error() != detail::extract_error(exp) : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<=(const result& exp, const failure& error) noexcept -> bool { return exp.has_error() ? detail::extract_error(exp) <= error.error() : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<=(const failure& error, const result& exp) noexcept -> bool { return exp.has_error() ? error.error() <= detail::extract_error(exp) : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>=(const result& exp, const failure& error) noexcept -> bool { return exp.has_error() ? detail::extract_error(exp) >= error.error() : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>=(const failure& error, const result& exp) noexcept -> bool { return exp.has_error() ? error.error() >= detail::extract_error(exp) : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<(const result& exp, const failure& error) noexcept -> bool { return exp.has_error() ? detail::extract_error(exp) < error.error() : true; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator<(const failure& error, const result& exp) noexcept -> bool { return exp.has_error() ? error.error() < detail::extract_error(exp) : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>(const result& exp, const failure& error) noexcept -> bool { return exp.has_error() ? detail::extract_error(exp) > error.error() : false; } template inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::operator>(const failure& error, const result& exp) noexcept -> bool { return exp.has_error() ? error.error() > detail::extract_error(exp) : true; } //----------------------------------------------------------------------------- // Utilities //----------------------------------------------------------------------------- template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::swap(result& lhs, result& rhs) #if __cplusplus >= 201703L noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value && std::is_nothrow_swappable::value && std::is_nothrow_swappable::value) #else noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value) #endif -> void { using std::swap; if (lhs.has_value() == rhs.has_value()) { if (lhs.has_value()) { swap(*lhs, *rhs); } else { auto& lhs_error = detail::result_error_extractor::get(lhs); auto& rhs_error = detail::result_error_extractor::get(rhs); swap(lhs_error, rhs_error); } // If both `result`s contain values, do nothing } else { auto temp = static_cast&&>(lhs); lhs = static_cast&&>(rhs); rhs = static_cast&&>(temp); } } template inline RESULT_INLINE_VISIBILITY auto RESULT_NS_IMPL::swap(result& lhs, result& rhs) #if __cplusplus >= 201703L noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value && std::is_nothrow_swappable::value) #else noexcept(std::is_nothrow_move_constructible>::value && std::is_nothrow_move_assignable>::value) #endif -> void { using std::swap; if (lhs.has_value() == rhs.has_value()) { if (lhs.has_error()) { auto& lhs_error = detail::result_error_extractor::get(lhs); auto& rhs_error = detail::result_error_extractor::get(rhs); swap(lhs_error, rhs_error); } // If both `result`s contain values, do nothing } else { auto temp = static_cast&&>(lhs); lhs = static_cast&&>(rhs); rhs = static_cast&&>(temp); } } #if defined(__clang__) # pragma clang diagnostic pop #endif #undef RESULT_NAMESPACE_INTERNAL #undef RESULT_NS_IMPL #undef RESULT_CPP14_CONSTEXPR #undef RESULT_CPP17_INLINE #undef RESULT_INLINE_VISIBILITY #undef RESULT_NODISCARD #undef RESULT_WARN_UNUSED #endif /* RESULT_RESULT_HPP */