| //////////////////////////////////////////////////////////////////////////////// |
| /// \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 <cstddef> // std::size_t |
| #include <type_traits> // std::enable_if, std::is_constructible, etc |
| #include <new> // placement-new |
| #include <memory> // std::address_of |
| #include <functional> // std::reference_wrapper, std::invoke |
| #include <utility> // std::in_place_t, std::forward |
| #include <initializer_list> // std::initializer_list |
| #include <string> // std::string (for exception message) |
| |
| #if defined(RESULT_EXCEPTIONS_DISABLED) |
| # include <cstdio> // std::fprintf, stderr |
| #else |
| # include <stdexcept> // 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 <typename T> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto forward(typename std::remove_reference<T>::type& t) |
| noexcept -> T&& |
| { |
| return static_cast<T&&>(t); |
| } |
| |
| template <typename T> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto forward(typename std::remove_reference<T>::type&& t) |
| noexcept -> T&& |
| { |
| return static_cast<T&&>(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<typename T> |
| struct is_reference_wrapper : std::false_type{}; |
| |
| template<typename U> |
| struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type{}; |
| |
| //------------------------------------------------------------------------- |
| |
| template <typename Base, typename T, typename Derived, typename... Args, |
| typename = typename std::enable_if< |
| std::is_function<T>::value && |
| std::is_base_of<Base, typename std::decay<Derived>::type>::value |
| >::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto invoke(T Base::*pmf, Derived&& ref, Args&&... args) |
| noexcept(noexcept((::RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmf)(::RESULT_NS_IMPL::detail::forward<Args>(args)...))) |
| -> decltype((::RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmf)(::RESULT_NS_IMPL::detail::forward<Args>(args)...)) |
| { |
| return (RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...); |
| } |
| |
| template <typename Base, typename T, typename RefWrap, typename... Args, |
| typename = typename std::enable_if< |
| std::is_function<T>::value && |
| is_reference_wrapper<typename std::decay<RefWrap>::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>(args)...))) |
| -> decltype((ref.get().*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...)) |
| { |
| return (ref.get().*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...); |
| } |
| |
| template <typename Base, typename T, typename Pointer, typename... Args, |
| typename = typename std::enable_if< |
| std::is_function<T>::value && |
| !is_reference_wrapper<typename std::decay<Pointer>::type>::value && |
| !std::is_base_of<Base, typename std::decay<Pointer>::type>::value |
| >::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto invoke(T Base::*pmf, Pointer&& ptr, Args&&... args) |
| noexcept(noexcept(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...))) |
| -> decltype(((*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...)) |
| { |
| return ((*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...); |
| } |
| |
| template <typename Base, typename T, typename Derived, |
| typename = typename std::enable_if< |
| !std::is_function<T>::value && |
| std::is_base_of<Base, typename std::decay<Derived>::type>::value |
| >::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto invoke(T Base::*pmd, Derived&& ref) |
| noexcept(noexcept(std::forward<Derived>(ref).*pmd)) |
| -> decltype(RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmd) |
| { |
| return RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmd; |
| } |
| |
| template <typename Base, typename T, typename RefWrap, |
| typename = typename std::enable_if< |
| !std::is_function<T>::value && |
| is_reference_wrapper<typename std::decay<RefWrap>::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 <typename Base, typename T, typename Pointer, |
| typename = typename std::enable_if< |
| !std::is_function<T>::value && |
| !is_reference_wrapper<typename std::decay<Pointer>::type>::value && |
| !std::is_base_of<Base, typename std::decay<Pointer>::type>::value |
| >::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto invoke(T Base::*pmd, Pointer&& ptr) |
| noexcept(noexcept((*std::forward<Pointer>(ptr)).*pmd)) |
| -> decltype((*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmd) |
| { |
| return (*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmd; |
| } |
| |
| template <typename F, typename... Args, |
| typename = typename std::enable_if<!std::is_member_pointer<typename std::decay<F>::type>::value>::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto invoke(F&& f, Args&&... args) |
| noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...))) |
| -> decltype(RESULT_NS_IMPL::detail::forward<F>(f)(RESULT_NS_IMPL::detail::forward<Args>(args)...)) |
| { |
| return RESULT_NS_IMPL::detail::forward<F>(f)(RESULT_NS_IMPL::detail::forward<Args>(args)...); |
| } |
| |
| template<typename Fn, typename...Args> |
| struct is_invocable |
| { |
| template <typename Fn2, typename...Args2> |
| static auto test( Fn2&&, Args2&&... ) |
| -> decltype(invoke(std::declval<Fn2>(), std::declval<Args2>()...), std::true_type{}); |
| |
| static auto test(...) |
| -> std::false_type; |
| |
| using type = decltype(test(std::declval<Fn>(), std::declval<Args>()...)); |
| static constexpr bool value = type::value; |
| }; |
| |
| template <bool B, typename Fn, typename...Args> |
| struct invoke_result_impl { |
| using type = decltype(RESULT_NS_IMPL::detail::invoke(std::declval<Fn>(), std::declval<Args>()...)); |
| }; |
| template <typename Fn, typename...Args> |
| struct invoke_result_impl<false, Fn, Args...>{}; |
| |
| template <typename Fn, typename...Args> |
| struct invoke_result |
| : invoke_result_impl<is_invocable<Fn,Args...>::value, Fn, Args...>{}; |
| |
| template <typename Fn, typename...Args> |
| using invoke_result_t = typename invoke_result<Fn, Args...>::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 <typename> |
| class failure; |
| |
| template <typename, typename> |
| class result; |
| |
| template <typename> |
| class bad_result_access; |
| |
| //=========================================================================== |
| // traits |
| //=========================================================================== |
| |
| template <typename T> |
| struct is_failure : std::false_type{}; |
| template <typename E> |
| struct is_failure<failure<E>> : std::true_type{}; |
| |
| template <typename T> |
| struct is_result : std::false_type{}; |
| template <typename T, typename E> |
| struct is_result<result<T,E>> : std::true_type{}; |
| |
| //=========================================================================== |
| // trait : detail::wrapped_result_type |
| //=========================================================================== |
| |
| namespace detail { |
| |
| template <typename T> |
| using wrapped_result_type = typename std::conditional< |
| std::is_lvalue_reference<T>::value, |
| std::reference_wrapper< |
| typename std::remove_reference<T>::type |
| >, |
| typename std::remove_const<T>::type |
| >::type; |
| |
| } // namespace detail |
| |
| #if !defined(RESULT_DISABLE_EXCEPTIONS) |
| |
| //=========================================================================== |
| // class : bad_result_access<E> |
| //=========================================================================== |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| /// \brief An exception thrown when result::value is accessed without |
| /// a contained value |
| ///////////////////////////////////////////////////////////////////////////// |
| template <typename E> |
| 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 <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2>::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 <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2>::value>::type> |
| bad_result_access(const char* what_arg, E2&& error); |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2>::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 <typename E, typename E2> |
| using failure_is_value_convertible = std::integral_constant<bool,( |
| std::is_constructible<E, E2&&>::value && |
| !std::is_same<typename std::decay<E2>::type, in_place_t>::value && |
| !is_failure<typename std::decay<E2>::type>::value && |
| !is_result<typename std::decay<E2>::type>::value |
| )>; |
| |
| template <typename E, typename E2> |
| using failure_is_explicit_value_convertible = std::integral_constant<bool,( |
| failure_is_value_convertible<E, E2>::value && |
| !std::is_convertible<E2, E>::value |
| )>; |
| |
| template <typename E, typename E2> |
| using failure_is_implicit_value_convertible = std::integral_constant<bool,( |
| failure_is_value_convertible<E, E2>::value && |
| std::is_convertible<E2, E>::value |
| )>; |
| |
| template <typename E, typename E2> |
| using failure_is_value_assignable = std::integral_constant<bool,( |
| !is_result<typename std::decay<E2>::type>::value && |
| !is_failure<typename std::decay<E2>::type>::value && |
| std::is_assignable<wrapped_result_type<E>&,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 <typename E> |
| class failure |
| { |
| static_assert( |
| !is_result<typename std::decay<E>::type>::value, |
| "A (possibly CV-qualified) result 'E' type is ill-formed." |
| ); |
| static_assert( |
| !is_failure<typename std::decay<E>::type>::value, |
| "A (possibly CV-qualified) failure 'E' type is ill-formed." |
| ); |
| static_assert( |
| !std::is_void<typename std::decay<E>::type>::value, |
| "A (possibly CV-qualified) 'void' 'E' type is ill-formed." |
| ); |
| static_assert( |
| !std::is_rvalue_reference<E>::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 <typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E,Args...>::value>::type> |
| constexpr failure(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::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 <typename U, typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E,std::initializer_list<U>,Args...>::value>::type> |
| constexpr failure(in_place_t, std::initializer_list<U> ilist, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value); |
| |
| /// \{ |
| /// \brief Constructs a failure from the given error |
| /// |
| /// \param error the error to create a failure from |
| template <typename E2, |
| typename std::enable_if<detail::failure_is_implicit_value_convertible<E,E2>::value,int>::type = 0> |
| constexpr failure(E2&& error) |
| noexcept(std::is_nothrow_constructible<E,E2>::value); |
| template <typename E2, |
| typename std::enable_if<detail::failure_is_explicit_value_convertible<E,E2>::value,int>::type = 0> |
| constexpr explicit failure(E2&& error) |
| noexcept(std::is_nothrow_constructible<E,E2>::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 <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,const E2&>::value>::type> |
| constexpr /* implicit */ failure(const failure<E2>& other) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value); |
| |
| /// \brief Constructs this failure by move-converting \p other |
| /// |
| /// \param other the other failure to copy |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2&&>::value>::type> |
| constexpr /* implicit */ failure(failure<E2>&& other) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::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 <typename E2, |
| typename = typename std::enable_if<detail::failure_is_value_assignable<E,E2>::value>::type> |
| RESULT_CPP14_CONSTEXPR |
| auto operator=(E2&& error) |
| noexcept(std::is_nothrow_assignable<E,E2>::value || std::is_lvalue_reference<E>::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 <typename E2, |
| typename = typename std::enable_if<std::is_assignable<E&,const E2&>::value>::type> |
| RESULT_CPP14_CONSTEXPR |
| auto operator=(const failure<E2>& other) |
| noexcept(std::is_nothrow_assignable<E,const E2&>::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 <typename E2, |
| typename = typename std::enable_if<std::is_assignable<E&,E2&&>::value>::type> |
| RESULT_CPP14_CONSTEXPR |
| auto operator=(failure<E2>&& other) |
| noexcept(std::is_nothrow_assignable<E,E2&&>::value) -> failure&; |
| |
| //-------------------------------------------------------------------------- |
| // Observers |
| //-------------------------------------------------------------------------- |
| public: |
| |
| /// \{ |
| /// \brief Gets the underlying error |
| /// |
| /// \return the underlying error |
| RESULT_CPP14_CONSTEXPR |
| auto error() & noexcept |
| -> typename std::add_lvalue_reference<E>::type; |
| RESULT_CPP14_CONSTEXPR |
| auto error() && noexcept |
| -> typename std::add_rvalue_reference<E>::type; |
| constexpr auto error() const & noexcept |
| -> typename std::add_lvalue_reference<typename std::add_const<E>::type>::type; |
| constexpr auto error() const && noexcept |
| -> typename std::add_rvalue_reference<typename std::add_const<E>::type>::type; |
| /// \} |
| |
| //------------------------------------------------------------------------- |
| // Private Member Types |
| //------------------------------------------------------------------------- |
| private: |
| |
| using underlying_type = detail::wrapped_result_type<E>; |
| |
| //------------------------------------------------------------------------- |
| // Private Members |
| //------------------------------------------------------------------------- |
| private: |
| |
| underlying_type m_failure; |
| }; |
| |
| #if __cplusplus >= 201703L |
| template <typename T> |
| failure(std::reference_wrapper<T>) -> failure<T&>; |
| |
| template <typename T> |
| failure(T&&) -> failure<typename std::decay<T>::type>; |
| #endif |
| |
| //=========================================================================== |
| // non-member functions : class : failure |
| //=========================================================================== |
| |
| //--------------------------------------------------------------------------- |
| // Comparison |
| //--------------------------------------------------------------------------- |
| |
| template <typename E1, typename E2> |
| constexpr auto operator==(const failure<E1>& lhs, |
| const failure<E2>& rhs) noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator!=(const failure<E1>& lhs, |
| const failure<E2>& rhs) noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator<(const failure<E1>& lhs, |
| const failure<E2>& rhs) noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator>(const failure<E1>& lhs, |
| const failure<E2>& rhs) noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator<=(const failure<E1>& lhs, |
| const failure<E2>& rhs) noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator>=(const failure<E1>& lhs, |
| const failure<E2>& 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 <typename E> |
| RESULT_WARN_UNUSED |
| constexpr auto fail(E&& e) |
| noexcept(std::is_nothrow_constructible<typename std::decay<E>::type,E>::value) |
| -> failure<typename std::decay<E>::type>; |
| |
| /// \brief Deduces a failure reference from a reverence_wrapper |
| /// |
| /// \param e the failure value |
| /// \return a constructed failure reference |
| template <typename E> |
| RESULT_WARN_UNUSED |
| constexpr auto fail(std::reference_wrapper<E> e) |
| noexcept -> failure<E&>; |
| |
| /// \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 <typename E, typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E,Args...>::value>::type> |
| RESULT_WARN_UNUSED |
| constexpr auto fail(Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| -> failure<E>; |
| |
| /// \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 <typename E, typename U, typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E,std::initializer_list<U>,Args...>::value>::type> |
| RESULT_WARN_UNUSED |
| constexpr auto fail(std::initializer_list<U> ilist, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value) |
| -> failure<E>; |
| |
| /// \brief Swaps the contents of two failure values |
| /// |
| /// \param lhs the left failure |
| /// \param rhs the right failure |
| template <typename E> |
| auto swap(failure<E>& lhs, failure<E>& rhs) |
| #if __cplusplus >= 201703L |
| noexcept(std::is_nothrow_swappable<E>::value) -> void; |
| #else |
| noexcept(std::is_nothrow_move_constructible<E>::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<T, E, IsTrivial> |
| //========================================================================= |
| |
| /////////////////////////////////////////////////////////////////////////// |
| /// \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 <typename T, typename E, |
| bool IsTrivial = std::is_trivially_destructible<T>::value && |
| std::is_trivially_destructible<E>::value> |
| struct result_union |
| { |
| //----------------------------------------------------------------------- |
| // Public Member Types |
| //----------------------------------------------------------------------- |
| |
| using underlying_value_type = wrapped_result_type<T>; |
| 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 <typename...Args> |
| constexpr result_union(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<T, Args...>::value); |
| |
| /// \brief Constructs the underlying error from the specified \p args |
| /// |
| /// \param args the arguments to forward to E's constructor |
| template <typename...Args> |
| constexpr result_union(in_place_error_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::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 <typename T, typename E> |
| struct result_union<T, E, false> |
| { |
| //----------------------------------------------------------------------- |
| // Public Member Types |
| //----------------------------------------------------------------------- |
| |
| using underlying_value_type = wrapped_result_type<T>; |
| 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 <typename...Args> |
| constexpr result_union(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<T, Args...>::value); |
| |
| /// \brief Constructs the underlying error from the specified \p args |
| /// |
| /// \param args the arguments to forward to E's constructor |
| template <typename...Args> |
| constexpr result_union(in_place_error_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::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<T>::value && |
| std::is_nothrow_destructible<E>::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<T, E> |
| //========================================================================= |
| |
| /////////////////////////////////////////////////////////////////////////// |
| /// \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 <typename T, typename E> |
| 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 <typename...Args> |
| constexpr result_construct_base(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<T, Args...>::value); |
| |
| /// \brief Constructs the underlying error from the specified \p args |
| /// |
| /// \param args the arguments to forward to E's constructor |
| template <typename...Args> |
| constexpr result_construct_base(in_place_error_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::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 <typename...Args> |
| auto construct_value(Args&&...args) |
| noexcept(std::is_nothrow_constructible<T,Args...>::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 <typename...Args> |
| auto construct_error(Args&&...args) |
| noexcept(std::is_nothrow_constructible<E,Args...>::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<void, E>` types |
| /// |
| /// \pre there is no contained value or error at the time of construction |
| /// |
| /// \param other the other result to construct |
| template <typename Result> |
| 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 <typename Result> |
| auto construct_from_result(Result&& other) -> void; |
| |
| //----------------------------------------------------------------------- |
| |
| template <typename Value> |
| auto assign_value(Value&& value) |
| noexcept(std::is_nothrow_assignable<T, Value>::value) -> void; |
| |
| template <typename Error> |
| auto assign_error(Error&& error) |
| noexcept(std::is_nothrow_assignable<E, Error>::value) -> void; |
| |
| template <typename Result> |
| auto assign_from_result(Result&& other) -> void; |
| |
| //----------------------------------------------------------------------- |
| |
| template <typename ReferenceWrapper> |
| auto construct_value_from_result_impl(std::true_type, ReferenceWrapper&& reference) |
| noexcept -> void; |
| |
| template <typename Value> |
| auto construct_value_from_result_impl(std::false_type, Value&& value) |
| noexcept(std::is_nothrow_constructible<T,Value>::value) -> void; |
| |
| template <typename Result> |
| auto assign_value_from_result_impl(std::true_type, Result&& other) -> void; |
| |
| template <typename Result> |
| auto assign_value_from_result_impl(std::false_type, Result&& other) -> void; |
| |
| //----------------------------------------------------------------------- |
| // Public Members |
| //----------------------------------------------------------------------- |
| |
| using storage_type = result_union<T, E>; |
| |
| storage_type storage; |
| }; |
| |
| //========================================================================= |
| // class : result_trivial_copy_ctor_base |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct result_trivial_copy_ctor_base_impl : result_construct_base<T,E> |
| { |
| using base_type = result_construct_base<T,E>; |
| 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<T>::value && |
| std::is_nothrow_copy_constructible<E>::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 <bool Condition, typename Base> |
| using conditionally_nest_type = typename std::conditional< |
| Condition, |
| typename Base::base_type, |
| Base |
| >::type; |
| |
| template <typename T, typename E> |
| using result_trivial_copy_ctor_base = conditionally_nest_type< |
| std::is_trivially_copy_constructible<T>::value && |
| std::is_trivially_copy_constructible<E>::value, |
| result_trivial_copy_ctor_base_impl<T,E> |
| >; |
| |
| //========================================================================= |
| // class : result_trivial_move_ctor_base |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct result_trivial_move_ctor_base_impl : result_trivial_copy_ctor_base<T,E> |
| { |
| using base_type = result_trivial_copy_ctor_base<T,E>; |
| 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<T>::value && |
| std::is_nothrow_move_constructible<E>::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 <typename T, typename E> |
| using result_trivial_move_ctor_base = conditionally_nest_type< |
| std::is_trivially_move_constructible<T>::value && |
| std::is_trivially_move_constructible<E>::value, |
| result_trivial_move_ctor_base_impl<T,E> |
| >; |
| |
| //========================================================================= |
| // class : result_trivial_copy_assign_base |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct result_trivial_copy_assign_base_impl |
| : result_trivial_move_ctor_base<T, E> |
| { |
| using base_type = result_trivial_move_ctor_base<T,E>; |
| 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<T>::value && |
| std::is_nothrow_copy_constructible<E>::value && |
| std::is_nothrow_copy_assignable<T>::value && |
| std::is_nothrow_copy_assignable<E>::value) |
| -> result_trivial_copy_assign_base_impl&; |
| auto operator=(result_trivial_copy_assign_base_impl&& other) |
| -> result_trivial_copy_assign_base_impl& = default; |
| }; |
| |
| template <typename T, typename E> |
| using result_trivial_copy_assign_base = conditionally_nest_type< |
| std::is_trivially_copy_constructible<T>::value && |
| std::is_trivially_copy_constructible<E>::value && |
| std::is_trivially_copy_assignable<T>::value && |
| std::is_trivially_copy_assignable<E>::value && |
| std::is_trivially_destructible<T>::value && |
| std::is_trivially_destructible<E>::value, |
| result_trivial_copy_assign_base_impl<T,E> |
| >; |
| |
| //========================================================================= |
| // class : result_trivial_move_assign_base |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct result_trivial_move_assign_base_impl |
| : result_trivial_copy_assign_base<T, E> |
| { |
| using base_type = result_trivial_copy_assign_base<T,E>; |
| 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<T>::value && |
| std::is_nothrow_move_constructible<E>::value && |
| std::is_nothrow_move_assignable<T>::value && |
| std::is_nothrow_move_assignable<E>::value) |
| -> result_trivial_move_assign_base_impl&; |
| }; |
| |
| template <typename T, typename E> |
| using result_trivial_move_assign_base = conditionally_nest_type< |
| std::is_trivially_move_constructible<T>::value && |
| std::is_trivially_move_constructible<E>::value && |
| std::is_trivially_move_assignable<T>::value && |
| std::is_trivially_move_assignable<E>::value && |
| std::is_trivially_destructible<T>::value && |
| std::is_trivially_destructible<E>::value, |
| result_trivial_move_assign_base_impl<T,E> |
| >; |
| |
| //========================================================================= |
| // class : disable_copy_ctor |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct disable_copy_ctor : result_trivial_move_assign_base<T,E> |
| { |
| using base_type = result_trivial_move_assign_base<T,E>; |
| 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 <typename T, typename E> |
| using result_copy_ctor_base = conditionally_nest_type< |
| std::is_copy_constructible<T>::value && |
| std::is_copy_constructible<E>::value, |
| disable_copy_ctor<T,E> |
| >; |
| |
| //========================================================================= |
| // class : disable_move_ctor |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct disable_move_ctor : result_copy_ctor_base<T,E> |
| { |
| using base_type = result_copy_ctor_base<T,E>; |
| 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 <typename T, typename E> |
| using result_move_ctor_base = conditionally_nest_type< |
| std::is_move_constructible<T>::value && |
| std::is_move_constructible<E>::value, |
| disable_move_ctor<T,E> |
| >; |
| |
| //========================================================================= |
| // class : disable_move_assignment |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct disable_move_assignment |
| : result_move_ctor_base<T, E> |
| { |
| using base_type = result_move_ctor_base<T, E>; |
| 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 <typename T, typename E> |
| using result_copy_assign_base = conditionally_nest_type< |
| std::is_nothrow_copy_constructible<T>::value && |
| std::is_nothrow_copy_constructible<E>::value && |
| std::is_copy_assignable<wrapped_result_type<T>>::value && |
| std::is_copy_assignable<E>::value, |
| disable_move_assignment<T,E> |
| >; |
| |
| //========================================================================= |
| // class : disable_copy_assignment |
| //========================================================================= |
| |
| template <typename T, typename E> |
| struct disable_copy_assignment |
| : result_copy_assign_base<T, E> |
| { |
| using base_type = result_copy_assign_base<T, E>; |
| 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 <typename T, typename E> |
| using result_move_assign_base = conditionally_nest_type< |
| std::is_nothrow_move_constructible<T>::value && |
| std::is_nothrow_move_constructible<E>::value && |
| std::is_move_assignable<wrapped_result_type<T>>::value && |
| std::is_move_assignable<E>::value, |
| disable_copy_assignment<T,E> |
| >; |
| |
| //========================================================================= |
| // alias : result_storage |
| //========================================================================= |
| |
| template <typename T, typename E> |
| using result_storage = result_move_assign_base<T, E>; |
| |
| //========================================================================= |
| // traits : result |
| //========================================================================= |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_convertible = std::integral_constant<bool,( |
| // T1 constructible from result<T2,E2> |
| std::is_constructible<T1, result<T2,E2>&>:: value || |
| std::is_constructible<T1, const result<T2,E2>&>:: value || |
| std::is_constructible<T1, result<T2,E2>&&>:: value || |
| std::is_constructible<T1, const result<T2,E2>&&>:: value || |
| |
| // E1 constructible from result<T2,E2> |
| std::is_constructible<E1, result<T2,E2>&>:: value || |
| std::is_constructible<E1, const result<T2,E2>&>:: value || |
| std::is_constructible<E1, result<T2,E2>&&>:: value || |
| std::is_constructible<E1, const result<T2,E2>&&>:: value || |
| |
| // result<T2,E2> convertible to T1 |
| std::is_convertible<result<T2,E2>&, T1>:: value || |
| std::is_convertible<const result<T2,E2>&, T1>:: value || |
| std::is_convertible<result<T2,E2>&&, T1>::value || |
| std::is_convertible<const result<T2,E2>&&, T1>::value || |
| |
| // result<T2,E2> convertible to E2 |
| std::is_convertible<result<T2,E2>&, E1>:: value || |
| std::is_convertible<const result<T2,E2>&, E1>:: value || |
| std::is_convertible<result<T2,E2>&&, E1>::value || |
| std::is_convertible<const result<T2,E2>&&, E1>::value |
| )>; |
| |
| //------------------------------------------------------------------------- |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_copy_convertible = std::integral_constant<bool,( |
| !result_is_convertible<T1,E1,T2,E2>::value && |
| std::is_constructible<T1, const T2&>::value && |
| std::is_constructible<E1, const E2&>::value |
| )>; |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_implicit_copy_convertible = std::integral_constant<bool,( |
| result_is_copy_convertible<T1,E1,T2,E2>::value && |
| std::is_convertible<const T2&, T1>::value && |
| std::is_convertible<const E2&, E1>::value |
| )>; |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_explicit_copy_convertible = std::integral_constant<bool,( |
| result_is_copy_convertible<T1,E1,T2,E2>::value && |
| (!std::is_convertible<const T2&, T1>::value || |
| !std::is_convertible<const E2&, E1>::value) |
| )>; |
| |
| //------------------------------------------------------------------------- |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_move_convertible = std::integral_constant<bool,( |
| !result_is_convertible<T1,E1,T2,E2>::value && |
| std::is_constructible<T1, T2&&>::value && |
| std::is_constructible<E1, E2&&>::value |
| )>; |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_implicit_move_convertible = std::integral_constant<bool,( |
| result_is_move_convertible<T1,E1,T2,E2>::value && |
| std::is_convertible<T2&&, T1>::value && |
| std::is_convertible<E2&&, E1>::value |
| )>; |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_explicit_move_convertible = std::integral_constant<bool,( |
| result_is_move_convertible<T1,E1,T2,E2>::value && |
| (!std::is_convertible<T2&&, T1>::value || |
| !std::is_convertible<E2&&, E1>::value) |
| )>; |
| |
| //------------------------------------------------------------------------- |
| |
| template <typename T, typename U> |
| using result_is_value_convertible = std::integral_constant<bool,( |
| std::is_constructible<T, U&&>::value && |
| !std::is_same<typename std::decay<U>::type, in_place_t>::value && |
| !std::is_same<typename std::decay<U>::type, in_place_error_t>::value && |
| !is_result<typename std::decay<U>::type>::value |
| )>; |
| |
| template <typename T, typename U> |
| using result_is_explicit_value_convertible = std::integral_constant<bool,( |
| result_is_value_convertible<T, U>::value && |
| !std::is_convertible<U&&, T>::value |
| )>; |
| |
| template <typename T, typename U> |
| using result_is_implicit_value_convertible = std::integral_constant<bool,( |
| result_is_value_convertible<T, U>::value && |
| std::is_convertible<U&&, T>::value |
| )>; |
| |
| //------------------------------------------------------------------------- |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_convert_assignable = std::integral_constant<bool,( |
| result_is_convertible<T1,E1,T2,E2>::value && |
| |
| std::is_assignable<T1&, result<T2,E2>&>::value && |
| std::is_assignable<T1&, const result<T2,E2>&>::value && |
| std::is_assignable<T1&, result<T2,E2>&&>::value && |
| std::is_assignable<T1&, const result<T2,E2>&&>::value && |
| |
| std::is_assignable<E1&, result<T2,E2>&>::value && |
| std::is_assignable<E1&, const result<T2,E2>&>::value && |
| std::is_assignable<E1&, result<T2,E2>&&>::value && |
| std::is_assignable<E1&, const result<T2,E2>&&>::value |
| )>; |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_copy_convert_assignable = std::integral_constant<bool,( |
| !result_is_convert_assignable<T1,E1,T2,E2>::value && |
| |
| std::is_nothrow_constructible<T1, const T2&>::value && |
| std::is_assignable<wrapped_result_type<T1>&, const T2&>::value && |
| std::is_nothrow_constructible<E1, const E2&>::value && |
| std::is_assignable<E1&, const E2&>::value |
| )>; |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| using result_is_move_convert_assignable = std::integral_constant<bool,( |
| !result_is_convert_assignable<T1,E1,T2,E2>::value && |
| |
| std::is_nothrow_constructible<T1, T2&&>::value && |
| std::is_assignable<T1&, T2&&>::value && |
| std::is_nothrow_constructible<E1, E2&&>::value && |
| std::is_assignable<E1&, E2&&>::value |
| )>; |
| |
| //------------------------------------------------------------------------- |
| |
| template <typename T, typename U> |
| using result_is_value_assignable = std::integral_constant<bool,( |
| !is_result<typename std::decay<U>::type>::value && |
| !is_failure<typename std::decay<U>::type>::value && |
| std::is_nothrow_constructible<T,U>::value && |
| std::is_assignable<wrapped_result_type<T>&,U>::value && |
| ( |
| !std::is_same<typename std::decay<U>::type,typename std::decay<T>::type>::value || |
| !std::is_scalar<T>::value |
| ) |
| )>; |
| |
| template <typename E, typename E2> |
| using result_is_failure_assignable = std::integral_constant<bool,( |
| std::is_nothrow_constructible<E,E2>::value && |
| std::is_assignable<E&,E2>::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 <typename T, typename E> |
| static constexpr auto get(const result<T,E>& exp) noexcept -> const E&; |
| template <typename T, typename E> |
| static constexpr auto get(result<T,E>& exp) noexcept -> E&; |
| }; |
| |
| template <typename T, typename E> |
| constexpr auto extract_error(const result<T,E>& exp) noexcept -> const E&; |
| |
| template <typename E> |
| [[noreturn]] |
| auto throw_bad_result_access(E&& error) -> void; |
| |
| template <typename String, typename E> |
| [[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<T,bool>` |
| /// 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<T,E>` 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<T,E>` 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<T,E>` 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<T,E>` 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<T,E>` 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<std::string> |
| /// { |
| /// 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 <typename T, typename E> |
| class RESULT_NODISCARD result |
| { |
| // Type requirements |
| |
| static_assert( |
| !std::is_abstract<T>::value, |
| "It is ill-formed for T to be abstract type" |
| ); |
| static_assert( |
| !std::is_same<typename std::decay<T>::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<typename std::decay<T>::type>::value, |
| "It is ill-formed for T to be a (possibly CV-qualified) 'result' type" |
| ); |
| static_assert( |
| !is_failure<typename std::decay<T>::type>::value, |
| "It is ill-formed for T to be a (possibly CV-qualified) 'failure' type" |
| ); |
| static_assert( |
| !std::is_rvalue_reference<T>::value, |
| "It is ill-formed for T to be an rvalue 'result type. " |
| "Only lvalue references are valid." |
| ); |
| |
| static_assert( |
| !std::is_abstract<E>::value, |
| "It is ill-formed for E to be abstract type" |
| ); |
| static_assert( |
| !std::is_void<typename std::decay<E>::type>::value, |
| "It is ill-formed for E to be (possibly CV-qualified) void type" |
| ); |
| static_assert( |
| !is_result<typename std::decay<E>::type>::value, |
| "It is ill-formed for E to be a (possibly CV-qualified) 'result' type" |
| ); |
| static_assert( |
| !is_failure<typename std::decay<E>::type>::value, |
| "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type" |
| ); |
| static_assert( |
| !std::is_same<typename std::decay<E>::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<E>::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 <typename T2, typename E2> |
| 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<E>; ///< The failure type |
| |
| template <typename U> |
| using rebind = result<U,E>; ///< 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,int>{} == std::string{}); |
| /// ``` |
| template <typename U=T, |
| typename = typename std::enable_if<std::is_constructible<U>::value>::type> |
| constexpr result() |
| noexcept(std::is_nothrow_constructible<U>::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<T>::value` or |
| /// `std::is_copy_constructible<E>::value` is `false` |
| /// |
| /// \note This constructor is defined as trivial if both |
| /// `std::is_trivially_copy_constructible<T>::value` and |
| /// `std::is_trivially_copy_constructible<E>::value` are `true` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// const auto r = cpp::result<int,int>{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<T>::value` or |
| /// `std::is_move_constructible<E>::value` is `false` |
| /// |
| /// \note This constructor is defined as trivial if both |
| /// `std::is_trivially_move_constructible<T>::value` and |
| /// `std::is_trivially_move_constructible<E>::value` are `true` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<std::string,int>{"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<T, const U&>` is `true` |
| /// - T is not constructible or convertible from any expression |
| /// of type (possibly const) `result<T2,E2>` |
| /// - E is not constructible or convertible from any expression |
| /// of type (possible const) `result<T2,E2>` |
| /// |
| /// \note This constructor is explicit if and only if |
| /// `std::is_convertible_v<const T2&, T>` or |
| /// `std::is_convertible_v<const E2&, E>` is `false` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// const auto r = cpp::result<int,int>{42}; |
| /// const auto s = cpp::result<long,long>{r}; |
| /// |
| /// assert(r == s); |
| /// ``` |
| /// |
| /// \param other the other type to convert |
| template <typename T2, typename E2, |
| typename std::enable_if<detail::result_is_implicit_copy_convertible<T,E,T2,E2>::value,int>::type = 0> |
| result(const result<T2,E2>& other) |
| noexcept(std::is_nothrow_constructible<T,const T2&>::value && |
| std::is_nothrow_constructible<E,const E2&>::value); |
| template <typename T2, typename E2, |
| typename std::enable_if<detail::result_is_explicit_copy_convertible<T,E,T2,E2>::value,int>::type = 0> |
| explicit result(const result<T2,E2>& other) |
| noexcept(std::is_nothrow_constructible<T,const T2&>::value && |
| std::is_nothrow_constructible<E,const E2&>::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<T, const U&>` is `true` |
| /// - T is not constructible or convertible from any expression |
| /// of type (possibly const) `result<T2,E2>` |
| /// - E is not constructible or convertible from any expression |
| /// of type (possible const) `result<T2,E2>` |
| /// |
| /// \note This constructor is explicit if and only if |
| /// `std::is_convertible_v<const T2&, T>` or |
| /// `std::is_convertible_v<const E2&, E>` is `false` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<std::unique_ptr<Derived>,int>{ |
| /// std::make_unique<Derived>() |
| /// }; |
| /// const auto s = cpp::result<std::unique_ptr<Base>,long>{ |
| /// std::move(r) |
| /// }; |
| /// ``` |
| /// |
| /// \param other the other type to convert |
| template <typename T2, typename E2, |
| typename std::enable_if<detail::result_is_implicit_move_convertible<T,E,T2,E2>::value,int>::type = 0> |
| result(result<T2,E2>&& other) |
| noexcept(std::is_nothrow_constructible<T,T2&&>::value && |
| std::is_nothrow_constructible<E,E2&&>::value); |
| template <typename T2, typename E2, |
| typename std::enable_if<detail::result_is_explicit_move_convertible<T,E,T2,E2>::value,int>::type = 0> |
| explicit result(result<T2,E2>&& other) |
| noexcept(std::is_nothrow_constructible<T,T2&&>::value && |
| std::is_nothrow_constructible<E,E2&&>::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>(args)...` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<std::string,int>{ |
| /// cpp::in_place, "Hello world" |
| /// }; |
| /// ``` |
| /// |
| /// \param args the arguments to pass to T's constructor |
| template <typename...Args, |
| typename = typename std::enable_if<std::is_constructible<T,Args...>::value>::type> |
| constexpr explicit result(in_place_t, Args&&... args) |
| noexcept(std::is_nothrow_constructible<T, Args...>::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<std::initializer_list<U>>(ilist)`, |
| /// `std::forward<Args>(args)...` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<std::string,int>{ |
| /// 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 <typename U, typename...Args, |
| typename = typename std::enable_if<std::is_constructible<T, std::initializer_list<U>&, Args...>::value>::type> |
| constexpr explicit result(in_place_t, |
| std::initializer_list<U> ilist, |
| Args&&...args) |
| noexcept(std::is_nothrow_constructible<T, std::initializer_list<U>, 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>(args)...` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<int,std::string>{ |
| /// cpp::in_place_error, "Hello world" |
| /// }; |
| /// ``` |
| /// |
| /// \param args the arguments to pass to E's constructor |
| template <typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E,Args...>::value>::type> |
| constexpr explicit result(in_place_error_t, Args&&... args) |
| noexcept(std::is_nothrow_constructible<E, 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<std::initializer_list<U>>(ilist)`, |
| /// `std::forward<Args>(args)...` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<int,std::string>{ |
| /// 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 <typename U, typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E, std::initializer_list<U>&, Args...>::value>::type> |
| constexpr explicit result(in_place_error_t, |
| std::initializer_list<U> ilist, |
| Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, 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<int, Error> r = Error(42); |
| /// |
| /// auto get_error_result() -> cpp::result<int,std::string> { |
| /// return "hello world!"; |
| /// } |
| /// ``` |
| /// |
| /// \param e the failure error |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,const E2&>::value>::type> |
| constexpr /* implicit */ result(const E2& e) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value); |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2&&>::value>::type> |
| constexpr /* implicit */ result(E2&& e) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::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<int,int> r = cpp::fail(42); |
| /// |
| /// auto get_error_result() -> cpp::result<int,std::string> { |
| /// return cpp::fail("hello world!"); |
| /// } |
| /// ``` |
| /// |
| /// \param e the failure error |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,const E2&>::value>::type> |
| constexpr /* implicit */ result(const failure<E2>& e) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value); |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2&&>::value>::type> |
| constexpr /* implicit */ result(failure<E2>&& e) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::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<T, U&&>` is true |
| /// and `decay_t<U>` 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<U&&, T>` is `false` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// cpp::result<int,int> r = 42; |
| /// |
| /// auto get_value() -> cpp::result<std::string,int> { |
| /// return "hello world!"; // implicit conversion |
| /// } |
| /// ``` |
| /// |
| /// \param value the value to copy |
| template <typename U, |
| typename std::enable_if<detail::result_is_explicit_value_convertible<T,U>::value,int>::type = 0> |
| constexpr explicit result(U&& value) |
| noexcept(std::is_nothrow_constructible<T,U>::value); |
| template <typename U, |
| typename std::enable_if<detail::result_is_implicit_value_convertible<T,U>::value,int>::type = 0> |
| constexpr /* implicit */ result(U&& value) |
| noexcept(std::is_nothrow_constructible<T,U>::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<T>` is `true`, and |
| /// - `std::is_nothrow_copy_constructible_v<E>` 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<T>::value` |
| /// - `std::is_trivially_copy_constructible<E>::value` |
| /// - `std::is_trivially_copy_assignable<T>::value` |
| /// - `std::is_trivially_copy_assignable<E>::value` |
| /// - `std::is_trivially_destructible<T>::value` |
| /// - `std::is_trivially_destructible<E>::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<T>` is `true`, and |
| /// - `std::is_nothrow_copy_constructible_v<E>` 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<T>::value` |
| /// - `std::is_trivially_move_constructible<E>::value` |
| /// - `std::is_trivially_move_assignable<T>::value` |
| /// - `std::is_trivially_move_assignable<E>::value` |
| /// - `std::is_trivially_destructible<T>::value` |
| /// - `std::is_trivially_destructible<E>::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<T, const T2&>`, |
| /// `std::is_assignable_v<T&, const T2&>`, |
| /// `std::is_nothrow_constructible_v<E, const E2&>`, |
| /// `std::is_assignable_v<E&, const E2&>` are all true. |
| /// - T is not constructible, convertible, or assignable from any |
| /// expression of type (possibly const) `result<T2,E2>` |
| /// |
| /// \param other the other result object to convert |
| /// \return reference to `(*this)` |
| template <typename T2, typename E2, |
| typename = typename std::enable_if<detail::result_is_copy_convert_assignable<T,E,T2,E2>::value>::type> |
| auto operator=(const result<T2,E2>& other) |
| noexcept(std::is_nothrow_assignable<T,const T2&>::value && |
| std::is_nothrow_assignable<E,const E2&>::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<T, T2&&>`, |
| /// `std::is_assignable_v<T&, T2&&>`, |
| /// `std::is_nothrow_constructible_v<E, E2&&>`, |
| /// `std::is_assignable_v<E&, E2&&>` are all true. |
| /// - T is not constructible, convertible, or assignable from any |
| /// expression of type (possibly const) `result<T2,E2>` |
| /// |
| /// \param other the other result object to convert |
| /// \return reference to `(*this)` |
| template <typename T2, typename E2, |
| typename = typename std::enable_if<detail::result_is_move_convert_assignable<T,E,T2,E2>::value>::type> |
| auto operator=(result<T2,E2>&& other) |
| noexcept(std::is_nothrow_assignable<T,T2&&>::value && |
| std::is_nothrow_assignable<E,E2&&>::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<U>(value) |
| /// or assigned from std::forward<U>(value). |
| /// |
| /// \note The function does not participate in overload resolution unless |
| /// - `std::decay_t<U>` is not a result type, |
| /// - `std::decay_t<U>` is not a failure type |
| /// - `std::is_nothrow_constructible_v<T, U>` is `true` |
| /// - `std::is_assignable_v<T&, U>` is `true` |
| /// - and at least one of the following is `true`: |
| /// - `T` is not a scalar type; |
| /// - `std::decay_t<U>` is not `T`. |
| /// |
| /// \param value to assign to the contained value |
| /// \return reference to `(*this)` |
| template <typename U, |
| typename = typename std::enable_if<detail::result_is_value_assignable<T,U>::value>::type> |
| auto operator=(U&& value) |
| noexcept(std::is_nothrow_assignable<T,U>::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<E, E2>` is `true`, and |
| /// - `std::is_assignable_v<E&, E2>` is `true` |
| /// |
| /// \param other the failure value to assign to this |
| /// \return reference to `(*this)` |
| template <typename E2, |
| typename = typename std::enable_if<detail::result_is_failure_assignable<E,const E2&>::value>::type> |
| auto operator=(const failure<E2>& other) |
| noexcept(std::is_nothrow_assignable<E, const E2&>::value) -> result&; |
| template <typename E2, |
| typename = typename std::enable_if<detail::result_is_failure_assignable<E,E2&&>::value>::type> |
| auto operator=(failure<E2>&& other) |
| noexcept(std::is_nothrow_assignable<E, E2&&>::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<Widget,int>{ |
| /// 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<T>::type*; |
| RESULT_WARN_UNUSED |
| constexpr auto operator->() |
| const noexcept -> typename std::remove_reference<typename std::add_const<T>::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<Widget,int>{ |
| /// 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<T>::type; |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto operator*() |
| && noexcept -> typename std::add_rvalue_reference<T>::type; |
| RESULT_WARN_UNUSED |
| constexpr auto operator*() |
| const& noexcept -> typename std::add_lvalue_reference<typename std::add_const<T>::type>::type; |
| RESULT_WARN_UNUSED |
| constexpr auto operator*() |
| const&& noexcept -> typename std::add_rvalue_reference<typename std::add_const<T>::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<int, int>; |
| /// auto r = get_result(); |
| /// if (r) { ... } |
| /// |
| /// assert(static_cast<bool>(cpp::result<int,int>{42})); |
| /// |
| /// assert(!static_cast<bool>(cpp::result<int,int>{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<int, int>; |
| /// auto r = get_result(); |
| /// if (r.has_value()) { ... } |
| /// |
| /// assert(cpp::result<int,int>{42}.has_value()); |
| /// |
| /// assert(!cpp::result<int,int>{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<int, int>; |
| /// |
| /// auto r = get_result(); |
| /// if (r.has_error()) { ... } |
| /// |
| /// assert(!cpp::result<int,int>{42}.has_error()); |
| /// |
| /// assert(cpp::result<int,int>{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<int,int>{42}.value() == 42); |
| /// |
| /// auto r = cpp::result<std::unique_ptr<int>,int>{ |
| /// std::make_unique<int>(42) |
| /// }; |
| /// auto s = std::move(r).value(); |
| /// |
| /// try { |
| /// auto r = cpp::result<int,int>{ cpp::fail(42) }; |
| /// auto v = r.value(); |
| /// } catch (const cpp::bad_result_access<int>& e) { |
| /// assert(e.error() == 42); |
| /// } |
| /// ``` |
| /// |
| /// \throws bad_result_access<E> 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<T>::type; |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto value() |
| && -> typename std::add_rvalue_reference<T>::type; |
| RESULT_WARN_UNUSED |
| constexpr auto value() |
| const & -> typename std::add_lvalue_reference<typename std::add_const<T>::type>::type; |
| RESULT_WARN_UNUSED |
| constexpr auto value() |
| const && -> typename std::add_rvalue_reference<typename std::add_const<T>::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<E>::value` is `true` |
| /// * `std::is_copy_constructible<E>::value` or |
| /// `std::is_move_constructible<E>::value` is `true` |
| /// * `E{}` represents the "good" (non-error) state |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<int,std::error_code>{ 42 }; |
| /// assert(r.error() == std::error_code{}); |
| /// |
| /// auto r = cpp::result<int,std::error_code>{ |
| /// 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<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E; |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto error() && |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_move_constructible<E>::value) -> E; |
| |
| RESULT_WARN_UNUSED |
| constexpr auto status() const & |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E; |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto status() && |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_move_constructible<E>::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<void,int>; |
| /// |
| /// start_service().expect("Service failed to start!"); |
| /// ``` |
| /// |
| /// \param message the message to provide to this expectation |
| template <typename String, |
| typename = typename std::enable_if<( |
| std::is_convertible<String,const std::string&>::value && |
| std::is_copy_constructible<E>::value |
| )>::type> |
| RESULT_CPP14_CONSTEXPR auto expect(String&& message) const & -> void; |
| template <typename String, |
| typename = typename std::enable_if<( |
| std::is_convertible<String,const std::string&>::value && |
| std::is_move_constructible<E>::value |
| )>::type> |
| RESULT_CPP14_CONSTEXPR auto expect(String&& message) && -> void; |
| /// \} |
| |
| //------------------------------------------------------------------------- |
| // 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<int,int>{42}; |
| /// assert(r.value_or(0) == 42); |
| /// |
| /// auto r = cpp::result<int,int>{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 <typename U> |
| RESULT_WARN_UNUSED |
| constexpr auto value_or(U&& default_value) |
| const & -> typename std::remove_reference<T>::type; |
| template <typename U> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto value_or(U&& default_value) |
| && -> typename std::remove_reference<T>::type; |
| /// \} |
| |
| /// \{ |
| /// \brief Returns the contained error if `*this` has an error, |
| /// otherwise returns \p default_error. |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<int,int>{42}; |
| /// assert(r.error_or(0) == cpp::fail(0)); |
| /// |
| /// auto r = cpp::result<int,int>{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 <typename U> |
| RESULT_WARN_UNUSED |
| constexpr auto error_or(U&& default_error) const & -> error_type; |
| template <typename U> |
| 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<int,int>{42}; |
| /// assert(r.and_then(100) == 100); |
| /// |
| /// auto r = cpp::result<int,int>{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 <typename U> |
| RESULT_WARN_UNUSED |
| constexpr auto and_then(U&& value) const -> result<typename std::decay<U>::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<std::string,int>; |
| /// auto r = cpp::result<int,int>{42}; |
| /// assert(r.flat_map(to_string) == "42"); |
| /// |
| /// auto r = cpp::result<int,int>{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 <typename Fn> |
| RESULT_WARN_UNUSED |
| constexpr auto flat_map(Fn&& fn) const & -> detail::invoke_result_t<Fn, const T&>; |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto flat_map(Fn&& fn) && -> detail::invoke_result_t<Fn, 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<int,int>{42}; |
| /// assert(r.map(to_string) == "42"); |
| /// |
| /// auto r = cpp::result<int,int>{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 <typename Fn> |
| RESULT_WARN_UNUSED |
| constexpr auto map(Fn&& fn) const & -> result<detail::invoke_result_t<Fn,const T&>,E>; |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto map(Fn&& fn) && -> result<detail::invoke_result_t<Fn,T&&>,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<int,int>{42}; |
| /// assert(r.map_error(to_string) == 42); |
| /// |
| /// auto r = cpp::result<int,int>{cpp::fail(42)}; |
| /// assert(r.map_error(to_string) == cpp::fail("42")); |
| /// |
| /// auto r = cpp::result<std::string,int>{}; |
| /// auto s = r.map(std::string::size); // 's' contains 'result<size_t,int>' |
| /// ``` |
| /// |
| /// \param fn the function to invoke with this |
| /// \return The result result of the function invoked |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| constexpr auto map_error(Fn&& fn) |
| const & -> result<T, detail::invoke_result_t<Fn,const E&>>; |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto map_error(Fn&& fn) |
| && -> result<T, detail::invoke_result_t<Fn,E&&>>; |
| /// \} |
| |
| /// \{ |
| /// \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<int,std::string>; |
| /// auto r = cpp::result<int,int>{42}; |
| /// assert(r.flat_map(to_string) == 42); |
| /// |
| /// auto r = cpp::result<int,int>{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 <typename Fn> |
| RESULT_WARN_UNUSED |
| constexpr auto flat_map_error(Fn&& fn) |
| const & -> detail::invoke_result_t<Fn, const E&>; |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn&& fn) |
| && -> detail::invoke_result_t<Fn, E&&>; |
| /// \} |
| |
| //------------------------------------------------------------------------- |
| // Private Members |
| //------------------------------------------------------------------------- |
| private: |
| |
| detail::result_storage<T,E> m_storage; |
| |
| //------------------------------------------------------------------------- |
| // Private Monadic Functions |
| //------------------------------------------------------------------------- |
| private: |
| |
| /// \{ |
| /// \brief Map implementations for void and non-void functions |
| /// |
| /// \param fn the function |
| template <typename Fn> |
| constexpr auto map_impl(std::true_type, Fn&& fn) const & -> result<void,E>; |
| template <typename Fn> |
| constexpr auto map_impl(std::false_type, Fn&& fn) const & -> result<detail::invoke_result_t<Fn,const T&>,E>; |
| template <typename Fn> |
| RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn&& fn) && -> result<void,E>; |
| template <typename Fn> |
| RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn&& fn) && -> result<detail::invoke_result_t<Fn,T&&>,E>; |
| /// \} |
| }; |
| |
| //=========================================================================== |
| // class : result<void,E> |
| //=========================================================================== |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| /// \brief Partial specialization of `result<void, E>` |
| /// |
| /// \tparam E the underlying error type |
| ///////////////////////////////////////////////////////////////////////////// |
| template <typename E> |
| class RESULT_NODISCARD result<void,E> |
| { |
| // Type requirements |
| |
| static_assert( |
| !std::is_void<typename std::decay<E>::type>::value, |
| "It is ill-formed for E to be (possibly CV-qualified) void type" |
| ); |
| static_assert( |
| !std::is_abstract<E>::value, |
| "It is ill-formed for E to be abstract type" |
| ); |
| static_assert( |
| !is_failure<typename std::decay<E>::type>::value, |
| "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type" |
| ); |
| static_assert( |
| !std::is_reference<E>::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 <typename T2, typename E2> |
| 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<E>; ///< The failure type |
| |
| template <typename U> |
| using rebind = result<U,E>; ///< Rebinds the result type |
| |
| //------------------------------------------------------------------------- |
| // Constructor / Assignment |
| //------------------------------------------------------------------------- |
| public: |
| |
| /// \brief Constructs a `result` object in a value state |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<void,int>{}; |
| /// ``` |
| 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<E>::value` is `false` |
| /// |
| /// \note This constructor is defined as trivial if both |
| /// `std::is_trivially_copy_constructible<E>::value` are `true` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// const auto r = cpp::result<void,int>{}; |
| /// 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<E>::value` is `false` |
| /// |
| /// \note This constructor is defined as trivial if both |
| /// `std::is_trivially_move_constructible<E>::value` are `true` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<void,std::string>{}; |
| /// 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<E, const E2&>` is `true` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// const auto r = cpp::result<int,int>{42}; |
| /// const auto s = cpp::result<void,int>{r}; |
| /// ``` |
| /// |
| /// \param other the other type to convert |
| template <typename U, typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2>::value>::type> |
| explicit result(const result<U,E2>& other) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::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<E, const E2&>` is `true` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<int,std::string>{42}; |
| /// auto s = cpp::result<void,std::string>{ |
| /// std::move(r) |
| /// }; |
| /// ``` |
| /// |
| /// \param other the other type to convert |
| template <typename U, typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2>::value>::type> |
| explicit result(result<U,E2>&& other) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::value); |
| |
| //------------------------------------------------------------------------- |
| |
| /// \brief Constructs a result object in a value state |
| /// |
| /// This constructor exists primarily for symmetry with the `result<T,E>` |
| /// constructor. Unlike the `T` overload, no variadic arguments may be |
| /// supplied. |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<void,std::string>{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>(args)...` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<void,std::string>{ |
| /// cpp::in_place_error, "Hello world" |
| /// }; |
| /// ``` |
| /// |
| /// \param args the arguments to pass to `E`'s constructor |
| template <typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E,Args...>::value>::type> |
| constexpr explicit result(in_place_error_t, Args&&... args) |
| noexcept(std::is_nothrow_constructible<E, 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<std::initializer_list<U>>(ilist)`, |
| /// `std::forward<Args>(args)...` |
| /// |
| /// ### Examples |
| /// |
| /// Basic Usage: |
| /// |
| /// ```cpp |
| /// auto r = cpp::result<void,std::string>{ |
| /// 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 <typename U, typename...Args, |
| typename = typename std::enable_if<std::is_constructible<E, std::initializer_list<U>&, Args...>::value>::type> |
| constexpr explicit result(in_place_error_t, |
| std::initializer_list<U> ilist, |
| Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, 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<void,int> r = cpp::fail(42); |
| /// |
| /// auto get_error_result() -> cpp::result<void,std::string> { |
| /// return cpp::fail("hello world!"); |
| /// } |
| /// ``` |
| /// |
| /// \param e the failure error |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,const E2&>::value>::type> |
| constexpr /* implicit */ result(const failure<E2>& e) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value); |
| template <typename E2, |
| typename = typename std::enable_if<std::is_constructible<E,E2&&>::value>::type> |
| constexpr /* implicit */ result(failure<E2>&& e) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::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<E>` 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<E>::value` |
| /// - `std::is_trivially_copy_assignable<E>::value` |
| /// - `std::is_trivially_destructible<E>::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<E>` 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<E>::value` |
| /// - `std::is_trivially_move_assignable<E>::value` |
| /// - `std::is_trivially_destructible<E>::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<E, const E2&>`, |
| /// `std::is_assignable_v<E&, const E2&>` are all `true`. |
| /// |
| /// \param other the other result object to convert |
| /// \return reference to `(*this)` |
| template <typename E2, |
| typename = typename std::enable_if<std::is_nothrow_constructible<E,const E2&>::value && |
| std::is_assignable<E&,const E2&>::value>::type> |
| auto operator=(const result<void,E2>& other) |
| noexcept(std::is_nothrow_assignable<E, const E2&>::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<E, E2&&>`, |
| /// `std::is_assignable_v<E&, E2&&>` are all `true`. |
| /// |
| /// \param other the other result object to convert |
| /// \return reference to `(*this)` |
| template <typename E2, |
| typename = typename std::enable_if<std::is_nothrow_constructible<E,E2&&>::value && |
| std::is_assignable<E&,E2&&>::value>::type> |
| auto operator=(result<void,E2>&& other) |
| noexcept(std::is_nothrow_assignable<E, E2&&>::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<E, E2>` is `true`, and |
| /// - `std::is_assignable_v<E&, E2>` is `true` |
| /// |
| /// \param other the failure value to assign to this |
| /// \return reference to `(*this)` |
| template <typename E2, |
| typename = typename std::enable_if<detail::result_is_failure_assignable<E,const E2&>::value>::type> |
| auto operator=(const failure<E2>& other) |
| noexcept(std::is_nothrow_assignable<E, const E2&>::value) -> result&; |
| template <typename E2, |
| typename = typename std::enable_if<detail::result_is_failure_assignable<E,E2&&>::value>::type> |
| auto operator=(failure<E2>&& other) |
| noexcept(std::is_nothrow_assignable<E, E2&&>::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<void, int>; |
| /// auto r = get_result(); |
| /// if (r) { ... } |
| /// |
| /// assert(static_cast<bool>(cpp::result<void,int>{})); |
| /// |
| /// assert(!static_cast<bool>(cpp::result<void,int>{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<T,E>::has_value |
| RESULT_WARN_UNUSED |
| constexpr auto has_value() const noexcept -> bool; |
| |
| /// \copydoc result<T,E>::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<T,E>` 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<void,int>{}.value(); // no exception |
| /// |
| /// auto r = cpp::result<void,std::unique_ptr<int>>{ |
| /// cpp::fail(std::make_unique<int>(42)) |
| /// }; |
| /// std::move(r).value(); // throws bad_result_access<std::unique_ptr<int>> |
| /// |
| /// try { |
| /// auto r = cpp::result<void,int>{ cpp::fail(42) }.value(); |
| /// } catch (const cpp::bad_result_access<int>& e) { |
| /// assert(e.error() == 42); |
| /// } |
| /// ``` |
| /// |
| /// \throws bad_result_access<E> if `*this` does not contain a value. |
| RESULT_CPP14_CONSTEXPR auto value() && -> void; |
| RESULT_CPP14_CONSTEXPR auto value() const & -> void; |
| /// \} |
| |
| /// \{ |
| /// \copydoc result<T,E>::error |
| RESULT_WARN_UNUSED |
| constexpr auto error() const & |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E; |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto error() && |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E; |
| /// \} |
| |
| /// \{ |
| /// \copydoc result<T,E>::expect |
| template <typename String, |
| typename = typename std::enable_if<( |
| std::is_convertible<String,const std::string&>::value && |
| std::is_copy_constructible<E>::value |
| )>::type> |
| RESULT_CPP14_CONSTEXPR auto expect(String&& message) const & -> void; |
| template <typename String, |
| typename = typename std::enable_if<( |
| std::is_convertible<String,const std::string&>::value && |
| std::is_move_constructible<E>::value |
| )>::type> |
| RESULT_CPP14_CONSTEXPR auto expect(String&& message) && -> void; |
| /// \} |
| |
| //------------------------------------------------------------------------- |
| // Monadic Functionalities |
| //------------------------------------------------------------------------- |
| public: |
| |
| /// \{ |
| /// \copydoc result<T,E>::error_or |
| template <typename U> |
| RESULT_WARN_UNUSED |
| constexpr auto error_or(U&& default_error) const & -> error_type; |
| template <typename U> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto error_or(U&& default_error) && -> error_type; |
| /// \} |
| |
| //------------------------------------------------------------------------- |
| |
| /// \copydoc result<T,E>::and_then |
| template <typename U> |
| RESULT_WARN_UNUSED |
| constexpr auto and_then(U&& value) const -> result<typename std::decay<U>::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<int,int> { return 42; } |
| /// auto r = cpp::result<void,int>{}; |
| /// assert(r.flat_map(generate_int) == 42); |
| /// |
| /// auto r = cpp::result<void,int>{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 <typename Fn> |
| RESULT_WARN_UNUSED |
| constexpr auto flat_map(Fn&& fn) const & -> detail::invoke_result_t<Fn>; |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto flat_map(Fn&& fn) && -> detail::invoke_result_t<Fn>; |
| /// \} |
| |
| /// \{ |
| /// \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<void,int>{}; |
| /// assert(r.map(generate_int) == 42); |
| /// |
| /// auto r = cpp::result<void,int>{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 <typename Fn> |
| RESULT_WARN_UNUSED |
| constexpr auto map(Fn&& fn) const & -> result<detail::invoke_result_t<Fn>,E>; |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto map(Fn&& fn) && -> result<detail::invoke_result_t<Fn>,E>; |
| /// \} |
| |
| /// \{ |
| /// \copydoc result<T,E>::map_error |
| template <typename Fn> |
| constexpr auto map_error(Fn&& fn) const & -> result<void, detail::invoke_result_t<Fn,const E&>>; |
| template <typename Fn> |
| RESULT_CPP14_CONSTEXPR |
| auto map_error(Fn&& fn) && -> result<void, detail::invoke_result_t<Fn,E&&>>; |
| /// \} |
| |
| |
| /// \{ |
| /// \copydoc result<T,E>::flat_map_error |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| constexpr auto flat_map_error(Fn&& fn) const & -> detail::invoke_result_t<Fn, const E&>; |
| template <typename Fn> |
| RESULT_WARN_UNUSED |
| RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn&& fn) && -> detail::invoke_result_t<Fn, E&&>; |
| /// \} |
| |
| //------------------------------------------------------------------------- |
| // Private Members |
| //------------------------------------------------------------------------- |
| private: |
| |
| detail::result_storage<detail::unit,E> m_storage; |
| |
| //------------------------------------------------------------------------- |
| // Private Monadic Functions |
| //------------------------------------------------------------------------- |
| private: |
| |
| /// \{ |
| /// \brief Map implementations for void and non-void functions |
| /// |
| /// \param fn the function |
| template <typename Fn> |
| constexpr auto map_impl(std::true_type, Fn&& fn) const & -> result<void,E>; |
| template <typename Fn> |
| constexpr auto map_impl(std::false_type, Fn&& fn) const & -> result<detail::invoke_result_t<Fn>,E>; |
| template <typename Fn> |
| RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn&& fn) && -> result<void,E>; |
| template <typename Fn> |
| RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn&& fn) && -> result<detail::invoke_result_t<Fn>,E>; |
| /// \} |
| }; |
| |
| //=========================================================================== |
| // non-member functions : class : result |
| //=========================================================================== |
| |
| //--------------------------------------------------------------------------- |
| // Comparison |
| //--------------------------------------------------------------------------- |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| constexpr auto operator==(const result<T1,E1>& lhs, const result<T2,E2>& rhs) |
| noexcept -> bool; |
| template <typename T1, typename E1, typename T2, typename E2> |
| constexpr auto operator!=(const result<T1,E1>& lhs, const result<T2,E2>& rhs) |
| noexcept -> bool; |
| template <typename T1, typename E1, typename T2, typename E2> |
| constexpr auto operator>=(const result<T1,E1>& lhs, const result<T2,E2>& rhs) |
| noexcept -> bool; |
| template <typename T1, typename E1, typename T2, typename E2> |
| constexpr auto operator<=(const result<T1,E1>& lhs, const result<T2,E2>& rhs) |
| noexcept -> bool; |
| template <typename T1, typename E1, typename T2, typename E2> |
| constexpr auto operator>(const result<T1,E1>& lhs, const result<T2,E2>& rhs) |
| noexcept -> bool; |
| template <typename T1, typename E1, typename T2, typename E2> |
| constexpr auto operator<(const result<T1,E1>& lhs, const result<T2,E2>& rhs) |
| noexcept -> bool; |
| |
| //--------------------------------------------------------------------------- |
| |
| template <typename E1, typename E2> |
| constexpr auto operator==(const result<void,E1>& lhs, const result<void,E2>& rhs) |
| noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator!=(const result<void,E1>& lhs, const result<void,E2>& rhs) |
| noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator>=(const result<void,E1>& lhs, const result<void,E2>& rhs) |
| noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator<=(const result<void,E1>& lhs, const result<void,E2>& rhs) |
| noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator>(const result<void,E1>& lhs, const result<void,E2>& rhs) |
| noexcept -> bool; |
| template <typename E1, typename E2> |
| constexpr auto operator<(const result<void,E1>& lhs, const result<void,E2>& rhs) |
| noexcept -> bool; |
| |
| //--------------------------------------------------------------------------- |
| |
| template <typename T, typename E, typename U, |
| typename = typename std::enable_if<!std::is_same<T,void>::value>::type> |
| constexpr auto operator==(const result<T,E>& exp, const U& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E, |
| typename = typename std::enable_if<!std::is_same<U,void>::value>::type> |
| constexpr auto operator==(const T& value, const result<U,E>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U, |
| typename = typename std::enable_if<!std::is_same<T,void>::value>::type> |
| constexpr auto operator!=(const result<T,E>& exp, const U& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E, |
| typename = typename std::enable_if<!std::is_same<U,void>::value>::type> |
| constexpr auto operator!=(const T& value, const result<U,E>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U, |
| typename = typename std::enable_if<!std::is_same<T,void>::value>::type> |
| constexpr auto operator<=(const result<T,E>& exp, const U& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E, |
| typename = typename std::enable_if<!std::is_same<U,void>::value>::type> |
| constexpr auto operator<=(const T& value, const result<U,E>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U, |
| typename = typename std::enable_if<!std::is_same<T,void>::value>::type> |
| constexpr auto operator>=(const result<T,E>& exp, const U& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E, |
| typename = typename std::enable_if<!std::is_same<U,void>::value>::type> |
| constexpr auto operator>=(const T& value, const result<U,E>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U, |
| typename = typename std::enable_if<!std::is_same<T,void>::value>::type> |
| constexpr auto operator<(const result<T,E>& exp, const U& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E, |
| typename = typename std::enable_if<!std::is_same<U,void>::value>::type> |
| constexpr auto operator<(const T& value, const result<U,E>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U, |
| typename = typename std::enable_if<!std::is_same<T,void>::value>::type> |
| constexpr auto operator>(const result<T,E>& exp, const U& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E, |
| typename = typename std::enable_if<!std::is_same<U,void>::value>::type> |
| constexpr auto operator>(const T& value, const result<U,E>& exp) |
| noexcept -> bool; |
| |
| //--------------------------------------------------------------------------- |
| |
| template <typename T, typename E, typename U> |
| constexpr auto operator==(const result<T,E>& exp, const failure<U>& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E> |
| constexpr auto operator==(const failure<T>& value, const result<E,U>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U> |
| constexpr auto operator!=(const result<T,E>& exp, const failure<U>& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E> |
| constexpr auto operator!=(const failure<T>& value, const result<E,U>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U> |
| constexpr auto operator<=(const result<T,E>& exp, const failure<U>& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E> |
| constexpr auto operator<=(const failure<T>& value, const result<E,U>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U> |
| constexpr auto operator>=(const result<T,E>& exp, const failure<U>& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E> |
| constexpr auto operator>=(const failure<T>& value, const result<E,U>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U> |
| constexpr auto operator<(const result<T,E>& exp, const failure<U>& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E> |
| constexpr auto operator<(const failure<T>& value, const result<E,U>& exp) |
| noexcept -> bool; |
| template <typename T, typename E, typename U> |
| constexpr auto operator>(const result<T,E>& exp, const failure<U>& value) |
| noexcept -> bool; |
| template <typename T, typename U, typename E> |
| constexpr auto operator>(const failure<T>& value, const result<E,U>& 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 <typename T, typename E> |
| auto swap(result<T,E>& lhs, result<T,E>& rhs) |
| #if __cplusplus >= 201703L |
| noexcept(std::is_nothrow_move_constructible<result<T,E>>::value && |
| std::is_nothrow_move_assignable<result<T,E>>::value && |
| std::is_nothrow_swappable<T>::value && |
| std::is_nothrow_swappable<E>::value) |
| #else |
| noexcept(std::is_nothrow_move_constructible<result<T,E>>::value && |
| std::is_nothrow_move_assignable<result<T,E>>::value) |
| #endif |
| -> void; |
| template <typename E> |
| auto swap(result<void,E>& lhs, result<void,E>& rhs) |
| #if __cplusplus >= 201703L |
| noexcept(std::is_nothrow_move_constructible<result<void,E>>::value && |
| std::is_nothrow_move_assignable<result<void,E>>::value && |
| std::is_nothrow_swappable<E>::value) |
| #else |
| noexcept(std::is_nothrow_move_constructible<result<void,E>>::value && |
| std::is_nothrow_move_assignable<result<void,E>>::value) |
| #endif |
| -> void; |
| /// \} |
| |
| } // inline namespace bitwizeshift |
| } // namespace EXPECTED_NAMESPACE |
| |
| namespace std { |
| |
| template <typename T, typename E> |
| struct hash<::RESULT_NS_IMPL::result<T,E>> |
| { |
| auto operator()(const RESULT_NS_IMPL::result<T,E>& x) const -> std::size_t |
| { |
| if (x.has_value()) { |
| return std::hash<T>{}(*x) + 1; // add '1' to differentiate from error case |
| } |
| return std::hash<E>{}(::RESULT_NS_IMPL::detail::extract_error(x)); |
| } |
| }; |
| |
| template <typename E> |
| struct hash<::RESULT_NS_IMPL::result<void,E>> |
| { |
| auto operator()(const RESULT_NS_IMPL::result<void,E>& x) const -> std::size_t |
| { |
| if (x.has_value()) { |
| return 0; |
| } |
| return std::hash<E>{}(::RESULT_NS_IMPL::detail::extract_error(x)); |
| } |
| }; |
| |
| } // namespace std |
| |
| #if !defined(RESULT_DISABLE_EXCEPTIONS) |
| |
| //============================================================================= |
| // class : bad_result_access |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Constructors |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::bad_result_access<E>::bad_result_access(E2&& error) |
| : logic_error{"error attempting to access value from result containing error"}, |
| m_error(detail::forward<E2>(error)) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::bad_result_access<E>::bad_result_access( |
| const char* what_arg, |
| E2&& error |
| ) : logic_error{what_arg}, |
| m_error(detail::forward<E2>(error)) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::bad_result_access<E>::bad_result_access( |
| const std::string& what_arg, |
| E2&& error |
| ) : logic_error{what_arg}, |
| m_error(detail::forward<E2>(error)) |
| { |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Observers |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::bad_result_access<E>::error() |
| & noexcept -> E& |
| { |
| return m_error; |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::bad_result_access<E>::error() |
| && noexcept -> E&& |
| { |
| return static_cast<E&&>(m_error); |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::bad_result_access<E>::error() |
| const & noexcept -> const E& |
| { |
| return m_error; |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::bad_result_access<E>::error() |
| const && noexcept -> const E&& |
| { |
| return static_cast<const E&&>(m_error); |
| } |
| |
| #endif |
| |
| //============================================================================= |
| // class : failure |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Constructors |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::failure<E>::failure(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| : m_failure(detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename U, typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::failure<E>::failure( |
| in_place_t, |
| std::initializer_list<U> ilist, |
| Args&&...args |
| ) noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value) |
| : m_failure(ilist, detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename E2, |
| typename std::enable_if<RESULT_NS_IMPL::detail::failure_is_explicit_value_convertible<E,E2>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::failure<E>::failure(E2&& error) |
| noexcept(std::is_nothrow_constructible<E,E2>::value) |
| : m_failure(detail::forward<E2>(error)) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename E2, |
| typename std::enable_if<RESULT_NS_IMPL::detail::failure_is_implicit_value_convertible<E,E2>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::failure<E>::failure(E2&& error) |
| noexcept(std::is_nothrow_constructible<E,E2>::value) |
| : m_failure(detail::forward<E2>(error)) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::failure<E>::failure(const failure<E2>& other) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value) |
| : m_failure(other.error()) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::failure<E>::failure(failure<E2>&& other) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::value) |
| : m_failure(static_cast<failure<E2>&&>(other).error()) |
| { |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::failure<E>::operator=(E2&& error) |
| noexcept( |
| std::is_nothrow_assignable<E,E2>::value || |
| std::is_lvalue_reference<E>::value |
| ) -> failure& |
| { |
| m_failure = detail::forward<E2>(error); |
| |
| return (*this); |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::failure<E>::operator=(const failure<E2>& other) |
| noexcept(std::is_nothrow_assignable<E,const E2&>::value) |
| -> failure& |
| { |
| m_failure = other.error(); |
| |
| return (*this); |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::failure<E>::operator=(failure<E2>&& other) |
| noexcept(std::is_nothrow_assignable<E,E2&&>::value) |
| -> failure& |
| { |
| m_failure = static_cast<failure<E2>&&>(other).error(); |
| |
| return (*this); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Observers |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::failure<E>::error() |
| & noexcept -> typename std::add_lvalue_reference<E>::type |
| { |
| return m_failure; |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::failure<E>::error() |
| && noexcept -> typename std::add_rvalue_reference<E>::type |
| { |
| using reference = typename std::add_rvalue_reference<E>::type; |
| |
| return static_cast<reference>(m_failure); |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::failure<E>::error() |
| const & noexcept |
| -> typename std::add_lvalue_reference<typename std::add_const<E>::type>::type |
| { |
| return m_failure; |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::failure<E>::error() |
| const && noexcept |
| -> typename std::add_rvalue_reference<typename std::add_const<E>::type>::type |
| { |
| using reference = typename std::add_rvalue_reference<typename std::add_const<E>::type>::type; |
| |
| return static_cast<reference>(m_failure); |
| } |
| |
| //============================================================================= |
| // non-member functions : class : failure |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Comparison |
| //----------------------------------------------------------------------------- |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator==(const failure<E1>& lhs, const failure<E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.error() == rhs.error(); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator!=(const failure<E1>& lhs, const failure<E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.error() != rhs.error(); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<(const failure<E1>& lhs, const failure<E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.error() < rhs.error(); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>(const failure<E1>& lhs, const failure<E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.error() > rhs.error(); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<=(const failure<E1>& lhs, const failure<E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.error() <= rhs.error(); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>=(const failure<E1>& lhs, const failure<E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.error() >= rhs.error(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Utilities |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::fail(E&& e) |
| noexcept(std::is_nothrow_constructible<typename std::decay<E>::type,E>::value) |
| -> failure<typename std::decay<E>::type> |
| { |
| using result_type = failure<typename std::decay<E>::type>; |
| |
| return result_type( |
| detail::forward<E>(e) |
| ); |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::fail(std::reference_wrapper<E> e) |
| noexcept -> failure<E&> |
| { |
| using result_type = failure<E&>; |
| |
| return result_type{e.get()}; |
| } |
| |
| template <typename E, typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::fail(Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| -> failure<E> |
| { |
| return failure<E>(in_place, detail::forward<Args>(args)...); |
| } |
| |
| template <typename E, typename U, typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::fail(std::initializer_list<U> ilist, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value) |
| -> failure<E> |
| { |
| return failure<E>(in_place, ilist, detail::forward<Args>(args)...); |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::swap(failure<E>& lhs, failure<E>& rhs) |
| #if __cplusplus >= 201703L |
| noexcept(std::is_nothrow_swappable<E>::value) -> void |
| #else |
| noexcept(std::is_nothrow_move_constructible<E>::value) |
| -> void |
| #endif |
| { |
| using std::swap; |
| |
| swap(lhs.error(), rhs.error()); |
| } |
| |
| //============================================================================= |
| // class : detail::result_union<T, E, IsTrivial> |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Constructors / Assignment |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E, bool IsTrivial> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_union<T, E, IsTrivial> |
| ::result_union(unit) |
| noexcept |
| : m_empty{} |
| { |
| // m_has_value intentionally not set |
| } |
| |
| template <typename T, typename E, bool IsTrivial> |
| template <typename...Args> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::detail::result_union<T,E,IsTrivial> |
| ::result_union(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<T, Args...>::value) |
| : m_value(detail::forward<Args>(args)...), |
| m_has_value{true} |
| { |
| } |
| |
| template <typename T, typename E, bool IsTrivial> |
| template <typename...Args> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::detail::result_union<T,E,IsTrivial> |
| ::result_union(in_place_error_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| : m_error(detail::forward<Args>(args)...), |
| m_has_value{false} |
| { |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Modifiers |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E, bool IsTrivial> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_union<T, E, IsTrivial>::destroy() |
| const noexcept -> void |
| { |
| // do nothing |
| } |
| |
| //============================================================================= |
| // class : detail::result_union<T, E, false> |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Constructors / Destructor / Assignment |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_union<T, E, false> |
| ::result_union(unit) |
| noexcept |
| : m_empty{} |
| { |
| // m_has_value intentionally not set |
| } |
| |
| template <typename T, typename E> |
| template <typename...Args> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::detail::result_union<T,E,false> |
| ::result_union(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<T, Args...>::value) |
| : m_value(detail::forward<Args>(args)...), |
| m_has_value{true} |
| { |
| } |
| |
| template <typename T, typename E> |
| template <typename...Args> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::detail::result_union<T,E,false> |
| ::result_union(in_place_error_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| : m_error(detail::forward<Args>(args)...), |
| m_has_value{false} |
| { |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_union<T,E,false> |
| ::~result_union() |
| noexcept(std::is_nothrow_destructible<T>::value && std::is_nothrow_destructible<E>::value) |
| { |
| destroy(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Modifiers |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_union<T, E, false>::destroy() |
| -> void |
| { |
| if (m_has_value) { |
| m_value.~underlying_value_type(); |
| } else { |
| m_error.~underlying_error_type(); |
| } |
| } |
| |
| //============================================================================= |
| // class : result_construct_base<T, E> |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Constructors / Assignment |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_construct_base<T,E>::result_construct_base(unit) |
| noexcept |
| : storage{unit{}} |
| { |
| } |
| |
| template <typename T, typename E> |
| template <typename...Args> |
| inline constexpr RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_construct_base<T,E>::result_construct_base( |
| in_place_t, |
| Args&&...args |
| ) noexcept(std::is_nothrow_constructible<T, Args...>::value) |
| : storage{in_place, detail::forward<Args>(args)...} |
| { |
| } |
| |
| template <typename T, typename E> |
| template <typename...Args> |
| inline constexpr RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_construct_base<T,E>::result_construct_base( |
| in_place_error_t, |
| Args&&...args |
| ) noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| : storage(in_place_error, detail::forward<Args>(args)...) |
| { |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Construction / Assignment |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename...Args> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::construct_value(Args&&...args) |
| noexcept(std::is_nothrow_constructible<T,Args...>::value) |
| -> void |
| { |
| using value_type = typename storage_type::underlying_value_type; |
| |
| auto* p = static_cast<void*>(std::addressof(storage.m_value)); |
| new (p) value_type(detail::forward<Args>(args)...); |
| storage.m_has_value = true; |
| } |
| |
| template <typename T, typename E> |
| template <typename...Args> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::construct_error(Args&&...args) |
| noexcept(std::is_nothrow_constructible<E,Args...>::value) |
| -> void |
| { |
| using error_type = typename storage_type::underlying_error_type; |
| |
| auto* p = static_cast<void*>(std::addressof(storage.m_error)); |
| new (p) error_type(detail::forward<Args>(args)...); |
| storage.m_has_value = false; |
| } |
| |
| template <typename T, typename E> |
| template <typename Result> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::construct_error_from_result( |
| Result&& other |
| ) -> void |
| { |
| if (other.storage.m_has_value) { |
| construct_value(); |
| } else { |
| construct_error(detail::forward<Result>(other).storage.m_error); |
| } |
| } |
| |
| |
| template <typename T, typename E> |
| template <typename Result> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::construct_from_result( |
| Result&& other |
| ) -> void |
| { |
| if (other.storage.m_has_value) { |
| construct_value_from_result_impl( |
| std::is_lvalue_reference<T>{}, |
| detail::forward<Result>(other).storage.m_value |
| ); |
| } else { |
| construct_error(detail::forward<Result>(other).storage.m_error); |
| } |
| } |
| |
| template <typename T, typename E> |
| template <typename Value> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::assign_value(Value&& value) |
| noexcept(std::is_nothrow_assignable<T,Value>::value) |
| -> void |
| { |
| if (!storage.m_has_value) { |
| storage.destroy(); |
| construct_value(detail::forward<Value>(value)); |
| } else { |
| storage.m_value = detail::forward<Value>(value); |
| } |
| } |
| |
| template <typename T, typename E> |
| template <typename Error> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::assign_error(Error&& error) |
| noexcept(std::is_nothrow_assignable<E,Error>::value) |
| -> void |
| { |
| if (storage.m_has_value) { |
| storage.destroy(); |
| construct_error(detail::forward<Error>(error)); |
| } else { |
| storage.m_error = detail::forward<Error>(error); |
| } |
| } |
| |
| template <typename T, typename E> |
| template <typename Result> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::assign_from_result(Result&& other) |
| -> void |
| { |
| if (other.storage.m_has_value != storage.m_has_value) { |
| storage.destroy(); |
| construct_from_result(detail::forward<Result>(other)); |
| } else if (storage.m_has_value) { |
| assign_value_from_result_impl( |
| std::is_lvalue_reference<T>{}, |
| detail::forward<Result>(other) |
| ); |
| } else { |
| storage.m_error = detail::forward<Result>(other).storage.m_error; |
| } |
| } |
| |
| template <typename T, typename E> |
| template <typename ReferenceWrapper> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::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<void*>(std::addressof(storage.m_value)); |
| new (p) value_type(reference.get()); |
| storage.m_has_value = true; |
| } |
| |
| template <typename T, typename E> |
| template <typename Value> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::construct_value_from_result_impl( |
| std::false_type, |
| Value&& value |
| ) noexcept(std::is_nothrow_constructible<T,Value>::value) -> void |
| { |
| using value_type = typename storage_type::underlying_value_type; |
| |
| auto* p = static_cast<void*>(std::addressof(storage.m_value)); |
| new (p) value_type(detail::forward<Value>(value)); |
| storage.m_has_value = true; |
| } |
| |
| template <typename T, typename E> |
| template <typename Result> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::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 <typename T, typename E> |
| template <typename Result> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_construct_base<T,E>::assign_value_from_result_impl( |
| std::false_type, |
| Result&& other |
| ) -> void |
| { |
| storage.m_value = detail::forward<Result>(other).storage.m_value; |
| } |
| |
| |
| //============================================================================= |
| // class : result_trivial_copy_ctor_base_impl |
| //============================================================================= |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_trivial_copy_ctor_base_impl<T,E> |
| ::result_trivial_copy_ctor_base_impl(const result_trivial_copy_ctor_base_impl& other) |
| noexcept(std::is_nothrow_copy_constructible<T>::value && |
| std::is_nothrow_copy_constructible<E>::value) |
| : base_type(unit{}) |
| { |
| using ctor_base = result_construct_base<T,E>; |
| |
| ctor_base::construct_from_result(static_cast<const ctor_base&>(other)); |
| } |
| |
| //============================================================================= |
| // class : result_trivial_move_ctor_base |
| //============================================================================= |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::detail::result_trivial_move_ctor_base_impl<T, E> |
| ::result_trivial_move_ctor_base_impl(result_trivial_move_ctor_base_impl&& other) |
| noexcept(std::is_nothrow_move_constructible<T>::value && |
| std::is_nothrow_move_constructible<E>::value) |
| : base_type(unit{}) |
| { |
| using ctor_base = result_construct_base<T,E>; |
| |
| ctor_base::construct_from_result(static_cast<ctor_base&&>(other)); |
| } |
| |
| //============================================================================= |
| // class : result_copy_assign_base |
| //============================================================================= |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_trivial_copy_assign_base_impl<T, E> |
| ::operator=(const result_trivial_copy_assign_base_impl& other) |
| noexcept(std::is_nothrow_copy_constructible<T>::value && |
| std::is_nothrow_copy_constructible<E>::value && |
| std::is_nothrow_copy_assignable<T>::value && |
| std::is_nothrow_copy_assignable<E>::value) |
| -> result_trivial_copy_assign_base_impl& |
| { |
| using ctor_base = result_construct_base<T,E>; |
| |
| ctor_base::assign_from_result(static_cast<const ctor_base&>(other)); |
| return (*this); |
| } |
| |
| //========================================================================= |
| // class : result_move_assign_base |
| //========================================================================= |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::detail::result_trivial_move_assign_base_impl<T, E> |
| ::operator=(result_trivial_move_assign_base_impl&& other) |
| noexcept(std::is_nothrow_move_constructible<T>::value && |
| std::is_nothrow_move_constructible<E>::value && |
| std::is_nothrow_move_assignable<T>::value && |
| std::is_nothrow_move_assignable<E>::value) |
| -> result_trivial_move_assign_base_impl& |
| { |
| using ctor_base = result_construct_base<T,E>; |
| |
| ctor_base::assign_from_result(static_cast<ctor_base&&>(other)); |
| return (*this); |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::detail::result_error_extractor::get(const result<T,E>& exp) |
| noexcept -> const E& |
| { |
| return exp.m_storage.storage.m_error; |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::detail::result_error_extractor::get(result<T,E>& exp) |
| noexcept -> E& |
| { |
| return exp.m_storage.storage.m_error; |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::detail::extract_error(const result<T,E>& exp) noexcept -> const E& |
| { |
| return result_error_extractor::get(exp); |
| } |
| |
| template <typename E> |
| 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<E>::type |
| >::type |
| >; |
| |
| throw exception_type{ |
| error.message(), |
| detail::forward<E>(error) |
| }; |
| #endif |
| } |
| |
| template <typename String, typename E> |
| 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<String>(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<E>::type |
| >::type |
| >; |
| |
| throw exception_type{ |
| detail::forward<String>(message), |
| detail::forward<E>(error) |
| }; |
| #endif |
| } |
| |
| //============================================================================= |
| // class : result<T,E> |
| //============================================================================= |
| |
| template <typename T, typename E> |
| template <typename U, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result() |
| noexcept(std::is_nothrow_constructible<U>::value) |
| : m_storage(in_place) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename T2, typename E2, |
| typename std::enable_if<RESULT_NS_IMPL::detail::result_is_implicit_copy_convertible<T,E,T2,E2>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::result<T, E>::result(const result<T2,E2>& other) |
| noexcept(std::is_nothrow_constructible<T,const T2&>::value && |
| std::is_nothrow_constructible<E,const E2&>::value) |
| : m_storage(detail::unit{}) |
| { |
| m_storage.construct_from_result( |
| static_cast<const result<T2,E2>&>(other).m_storage |
| ); |
| } |
| |
| template <typename T, typename E> |
| template <typename T2, typename E2, |
| typename std::enable_if<RESULT_NS_IMPL::detail::result_is_explicit_copy_convertible<T,E,T2,E2>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::result<T, E>::result(const result<T2,E2>& other) |
| noexcept(std::is_nothrow_constructible<T,const T2&>::value && |
| std::is_nothrow_constructible<E,const E2&>::value) |
| : m_storage(detail::unit{}) |
| { |
| m_storage.construct_from_result( |
| static_cast<const result<T2,E2>&>(other).m_storage |
| ); |
| } |
| |
| template <typename T, typename E> |
| template <typename T2, typename E2, |
| typename std::enable_if<RESULT_NS_IMPL::detail::result_is_implicit_move_convertible<T,E,T2,E2>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::result<T, E>::result(result<T2,E2>&& other) |
| noexcept(std::is_nothrow_constructible<T,T2&&>::value && |
| std::is_nothrow_constructible<E,E2&&>::value) |
| : m_storage(detail::unit{}) |
| { |
| m_storage.construct_from_result( |
| static_cast<result<T2,E2>&&>(other).m_storage |
| ); |
| } |
| |
| template <typename T, typename E> |
| template <typename T2, typename E2, |
| typename std::enable_if<RESULT_NS_IMPL::detail::result_is_explicit_move_convertible<T,E,T2,E2>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::result<T, E>::result(result<T2,E2>&& other) |
| noexcept(std::is_nothrow_constructible<T,T2&&>::value && |
| std::is_nothrow_constructible<E,E2&&>::value) |
| : m_storage(detail::unit{}) |
| { |
| m_storage.construct_from_result( |
| static_cast<result<T2,E2>&&>(other).m_storage |
| ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(in_place_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<T, Args...>::value) |
| : m_storage(in_place, detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename U, typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result( |
| in_place_t, |
| std::initializer_list<U> ilist, |
| Args&&...args |
| ) noexcept(std::is_nothrow_constructible<T, std::initializer_list<U>, Args...>::value) |
| : m_storage(in_place, ilist, detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(in_place_error_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| : m_storage(in_place_error, detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename U, typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result( |
| in_place_error_t, |
| std::initializer_list<U> ilist, |
| Args&&...args |
| ) noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value) |
| : m_storage(in_place_error, ilist, detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| //------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(const failure<E2>& e) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value) |
| : m_storage(in_place_error, e.error()) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(failure<E2>&& e) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::value) |
| : m_storage(in_place_error, static_cast<E2&&>(e.error())) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(const E2& e) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value) |
| : m_storage(in_place_error, e) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(E2&& e) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::value) |
| : m_storage(in_place_error, static_cast<E2&&>(e)) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename U, |
| typename std::enable_if<RESULT_NS_IMPL::detail::result_is_explicit_value_convertible<T,U>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(U&& value) |
| noexcept(std::is_nothrow_constructible<T,U>::value) |
| : m_storage(in_place, detail::forward<U>(value)) |
| { |
| |
| } |
| |
| template <typename T, typename E> |
| template <typename U, |
| typename std::enable_if<RESULT_NS_IMPL::detail::result_is_implicit_value_convertible<T,U>::value,int>::type> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T, E>::result(U&& value) |
| noexcept(std::is_nothrow_constructible<T,U>::value) |
| : m_storage(in_place, detail::forward<U>(value)) |
| { |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename T2, typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<T, E>::operator=(const result<T2,E2>& other) |
| noexcept(std::is_nothrow_assignable<T, const T2&>::value && |
| std::is_nothrow_assignable<E, const E2&>::value) |
| -> result& |
| { |
| m_storage.assign_from_result( |
| static_cast<const result<T2,E2>&>(other).m_storage |
| ); |
| return (*this); |
| } |
| |
| template <typename T, typename E> |
| template <typename T2, typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<T, E>::operator=(result<T2,E2>&& other) |
| noexcept(std::is_nothrow_assignable<T, T2&&>::value && |
| std::is_nothrow_assignable<E, E2&&>::value) |
| -> result& |
| { |
| m_storage.assign_from_result( |
| static_cast<result<T2,E2>&&>(other).m_storage |
| ); |
| return (*this); |
| } |
| |
| template <typename T, typename E> |
| template <typename U, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<T, E>::operator=(U&& value) |
| noexcept(std::is_nothrow_assignable<T, U>::value) |
| -> result& |
| { |
| m_storage.assign_value(detail::forward<U>(value)); |
| return (*this); |
| } |
| |
| template <typename T, typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<T, E>::operator=(const failure<E2>& other) |
| noexcept(std::is_nothrow_assignable<E, const E2&>::value) |
| -> result& |
| { |
| m_storage.assign_error(other.error()); |
| return (*this); |
| } |
| |
| template <typename T, typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<T, E>::operator=(failure<E2>&& other) |
| noexcept(std::is_nothrow_assignable<E, E2&&>::value) |
| -> result& |
| { |
| m_storage.assign_error(static_cast<E2&&>(other.error())); |
| return (*this); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Observers |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::operator->() |
| noexcept -> typename std::remove_reference<T>::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 <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::operator->() |
| const noexcept -> typename std::remove_reference<typename std::add_const<T>::type>::type* |
| { |
| #if __cplusplus >= 201703L |
| return std::addressof(**this); |
| #else |
| return &(**this); |
| #endif |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::operator*() |
| & noexcept -> typename std::add_lvalue_reference<T>::type |
| { |
| return m_storage.storage.m_value; |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::operator*() |
| && noexcept -> typename std::add_rvalue_reference<T>::type |
| { |
| using reference = typename std::add_rvalue_reference<T>::type; |
| |
| return static_cast<reference>(m_storage.storage.m_value); |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::operator*() |
| const& noexcept -> typename std::add_lvalue_reference<typename std::add_const<T>::type>::type |
| { |
| return m_storage.storage.m_value; |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::operator*() |
| const&& noexcept -> typename std::add_rvalue_reference<typename std::add_const<T>::type>::type |
| { |
| using reference = typename std::add_rvalue_reference<typename std::add_const<T>::type>::type; |
| |
| return static_cast<reference>(m_storage.storage.m_value); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<T,E>::operator bool() |
| const noexcept |
| { |
| return m_storage.storage.m_has_value; |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T,E>::has_value() |
| const noexcept -> bool |
| { |
| return m_storage.storage.m_has_value; |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T,E>::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 <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T,E>::value() |
| & -> typename std::add_lvalue_reference<T>::type |
| { |
| return (has_value() || |
| (detail::throw_bad_result_access(m_storage.storage.m_error), false), |
| m_storage.storage.m_value |
| ); |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T,E>::value() |
| && -> typename std::add_rvalue_reference<T>::type |
| { |
| using reference = typename std::add_rvalue_reference<T>::type; |
| |
| return (has_value() || |
| (detail::throw_bad_result_access(static_cast<E&&>(m_storage.storage.m_error)), true), |
| static_cast<reference>(m_storage.storage.m_value) |
| ); |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T,E>::value() |
| const & -> typename std::add_lvalue_reference<typename std::add_const<T>::type>::type |
| { |
| return (has_value() || |
| (detail::throw_bad_result_access(m_storage.storage.m_error), true), |
| m_storage.storage.m_value |
| ); |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T,E>::value() |
| const && -> typename std::add_rvalue_reference<typename std::add_const<T>::type>::type |
| { |
| using reference = typename std::add_rvalue_reference<typename std::add_const<T>::type>::type; |
| |
| return (has_value() || |
| (detail::throw_bad_result_access(static_cast<const E&&>(m_storage.storage.m_error)), true), |
| (static_cast<reference>(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 <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T,E>::error() const & |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E |
| { |
| static_assert( |
| std::is_default_constructible<E>::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 <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T,E>::error() && |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_move_constructible<E>::value) -> E |
| { |
| static_assert( |
| std::is_default_constructible<E>::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<E&&>(m_storage.storage.m_error); |
| } |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T,E>::status() const & |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E |
| { |
| static_assert( |
| std::is_default_constructible<E>::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 <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T,E>::status() && |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_move_constructible<E>::value) -> E |
| { |
| static_assert( |
| std::is_default_constructible<E>::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<E&&>(m_storage.storage.m_error); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename String, typename> |
| inline RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T,E>::expect(String&& message) |
| const & -> void |
| { |
| if (has_error()) { |
| detail::throw_bad_result_access_message( |
| detail::forward<String>(message), |
| m_storage.storage.m_error |
| ); |
| } |
| } |
| |
| template <typename T, typename E> |
| template <typename String, typename> |
| inline RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T,E>::expect(String&& message) |
| && -> void |
| { |
| if (has_error()) { |
| detail::throw_bad_result_access_message( |
| detail::forward<String>(message), |
| static_cast<E&&>(m_storage.storage.m_error) |
| ); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Monadic Functionalities |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::value_or(U&& default_value) |
| const& -> typename std::remove_reference<T>::type |
| { |
| return m_storage.storage.m_has_value |
| ? m_storage.storage.m_value |
| : detail::forward<U>(default_value); |
| } |
| |
| template <typename T, typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::value_or(U&& default_value) |
| && -> typename std::remove_reference<T>::type |
| { |
| return m_storage.storage.m_has_value |
| ? static_cast<T&&>(**this) |
| : detail::forward<U>(default_value); |
| } |
| |
| template <typename T, typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::error_or(U&& default_error) |
| const& -> error_type |
| { |
| return m_storage.storage.m_has_value |
| ? detail::forward<U>(default_error) |
| : m_storage.storage.m_error; |
| } |
| |
| template <typename T, typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::error_or(U&& default_error) |
| && -> error_type |
| { |
| return m_storage.storage.m_has_value |
| ? detail::forward<U>(default_error) |
| : static_cast<E&&>(m_storage.storage.m_error); |
| } |
| |
| template <typename T, typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::and_then(U&& value) |
| const -> result<typename std::decay<U>::type,E> |
| { |
| return map([&value](const T&){ |
| return detail::forward<U>(value); |
| }); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::flat_map(Fn&& fn) |
| const & -> detail::invoke_result_t<Fn, const T&> |
| { |
| using result_type = detail::invoke_result_t<Fn, const T&>; |
| |
| static_assert( |
| is_result<result_type>::value, |
| "flat_map must return a result type or the program is ill-formed" |
| ); |
| |
| return has_value() |
| ? detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_value) |
| : result_type(in_place_error, m_storage.storage.m_error); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::flat_map(Fn&& fn) |
| && -> detail::invoke_result_t<Fn, T&&> |
| { |
| using result_type = detail::invoke_result_t<Fn, T&&>; |
| |
| static_assert( |
| is_result<result_type>::value, |
| "flat_map must return a result type or the program is ill-formed" |
| ); |
| |
| return has_value() |
| ? detail::invoke(detail::forward<Fn>(fn), static_cast<T&&>(m_storage.storage.m_value)) |
| : result_type(in_place_error, static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::map(Fn&& fn) |
| const & -> result<detail::invoke_result_t<Fn,const T&>,E> |
| { |
| using result_type = detail::invoke_result_t<Fn,const T&>; |
| |
| return map_impl(std::is_void<result_type>{}, detail::forward<Fn>(fn)); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::map(Fn&& fn) |
| && -> result<detail::invoke_result_t<Fn,T&&>,E> |
| { |
| using result_type = detail::invoke_result_t<Fn,T&&>; |
| |
| return static_cast<result<T,E>&&>(*this).map_impl( |
| std::is_void<result_type>{}, |
| detail::forward<Fn>(fn) |
| ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::map_error(Fn&& fn) |
| const & -> result<T, detail::invoke_result_t<Fn,const E&>> |
| { |
| using result_type = result<T, detail::invoke_result_t<Fn, const E&>>; |
| |
| return has_error() |
| ? result_type(in_place_error, detail::invoke( |
| detail::forward<Fn>(fn), m_storage.storage.m_error |
| )) |
| : result_type(in_place, m_storage.storage.m_value); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::map_error(Fn&& fn) |
| && -> result<T, detail::invoke_result_t<Fn,E&&>> |
| { |
| using result_type = result<T, detail::invoke_result_t<Fn, E&&>>; |
| |
| return has_error() |
| ? result_type(in_place_error, detail::invoke( |
| detail::forward<Fn>(fn), static_cast<E&&>(m_storage.storage.m_error) |
| )) |
| : result_type(static_cast<T&&>(m_storage.storage.m_value)); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::flat_map_error(Fn&& fn) |
| const & -> detail::invoke_result_t<Fn, const E&> |
| { |
| using result_type = detail::invoke_result_t<Fn, const E&>; |
| |
| static_assert( |
| is_result<result_type>::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>(fn), m_storage.storage.m_error); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::flat_map_error(Fn&& fn) |
| && -> detail::invoke_result_t<Fn, E&&> |
| { |
| using result_type = detail::invoke_result_t<Fn, E&&>; |
| |
| static_assert( |
| is_result<result_type>::value, |
| "flat_map_error must return a result type or the program is ill-formed" |
| ); |
| |
| return has_value() |
| ? result_type(in_place, static_cast<T&&>(m_storage.storage.m_value)) |
| : detail::invoke(detail::forward<Fn>(fn), static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Private Monadic Functions |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::map_impl(std::true_type, Fn&& fn) |
| const & -> result<void,E> |
| { |
| using result_type = result<void, E>; |
| |
| return has_value() |
| ? (detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_value), result_type{}) |
| : result_type(in_place_error, m_storage.storage.m_error); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<T, E>::map_impl(std::false_type, Fn&& fn) |
| const & -> result<detail::invoke_result_t<Fn,const T&>,E> |
| { |
| using invoke_result_type = detail::invoke_result_t<Fn,const T&>; |
| using result_type = result<invoke_result_type, E>; |
| |
| return has_value() |
| ? result_type(in_place, detail::invoke( |
| detail::forward<Fn>(fn), m_storage.storage.m_value |
| )) |
| : result_type(in_place_error, m_storage.storage.m_error); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::map_impl(std::true_type, Fn&& fn) |
| && -> result<void,E> |
| { |
| using result_type = result<void, E>; |
| |
| return has_value() |
| ? (detail::invoke( |
| detail::forward<Fn>(fn), static_cast<T&&>(m_storage.storage.m_value) |
| ), result_type{}) |
| : result_type(in_place_error, static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| template <typename T, typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<T, E>::map_impl(std::false_type, Fn&& fn) |
| && -> result<detail::invoke_result_t<Fn,T&&>,E> |
| { |
| using invoke_result_type = detail::invoke_result_t<Fn,T&&>; |
| using result_type = result<invoke_result_type, E>; |
| |
| return has_value() |
| ? result_type(in_place, detail::invoke( |
| detail::forward<Fn>(fn), static_cast<T&&>(m_storage.storage.m_value) |
| )) |
| : result_type(in_place_error, static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| //============================================================================= |
| // class : result<void,E> |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Constructor / Assignment |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<void, E>::result() |
| noexcept |
| : m_storage(in_place) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename U, typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::result<void, E>::result(const result<U,E2>& other) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value) |
| : m_storage(detail::unit{}) |
| { |
| m_storage.construct_error_from_result( |
| static_cast<const result<U,E2>&>(other).m_storage |
| ); |
| } |
| |
| template <typename E> |
| template <typename U, typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| RESULT_NS_IMPL::result<void, E>::result(result<U,E2>&& other) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::value) |
| : m_storage(detail::unit{}) |
| { |
| m_storage.construct_error_from_result( |
| static_cast<result<U,E2>&&>(other).m_storage |
| ); |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<void, E>::result(in_place_t) |
| noexcept |
| : m_storage(in_place) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<void, E>::result(in_place_error_t, Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, Args...>::value) |
| : m_storage(in_place_error, detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename U, typename...Args, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<void, E>::result(in_place_error_t, |
| std::initializer_list<U> ilist, |
| Args&&...args) |
| noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value) |
| : m_storage(in_place_error, ilist, detail::forward<Args>(args)...) |
| { |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<void, E>::result(const failure<E2>& e) |
| noexcept(std::is_nothrow_constructible<E,const E2&>::value) |
| : m_storage(in_place_error, e.error()) |
| { |
| |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<void, E>::result(failure<E2>&& e) |
| noexcept(std::is_nothrow_constructible<E,E2&&>::value) |
| : m_storage(in_place_error, static_cast<E2&&>(e.error())) |
| { |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<void, E>::operator=(const result<void,E2>& other) |
| noexcept(std::is_nothrow_assignable<E, const E2&>::value) |
| -> result& |
| { |
| m_storage.assign_from_result(other.m_storage); |
| return (*this); |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<void, E>::operator=(result<void,E2>&& other) |
| noexcept(std::is_nothrow_assignable<E, E2&&>::value) |
| -> result& |
| { |
| m_storage.assign_from_result(static_cast<result<void,E2>&&>(other).m_storage); |
| return (*this); |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<void, E>::operator=(const failure<E2>& other) |
| noexcept(std::is_nothrow_assignable<E, const E2&>::value) |
| -> result& |
| { |
| m_storage.assign_error(other.error()); |
| return (*this); |
| } |
| |
| template <typename E> |
| template <typename E2, typename> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::result<void, E>::operator=(failure<E2>&& other) |
| noexcept(std::is_nothrow_assignable<E, E2&&>::value) |
| -> result& |
| { |
| m_storage.assign_error(static_cast<E2&&>(other.error())); |
| return (*this); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Observers |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| RESULT_NS_IMPL::result<void, E>::operator bool() |
| const noexcept |
| { |
| return has_value(); |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::has_value() |
| const noexcept -> bool |
| { |
| return m_storage.storage.m_has_value; |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::has_error() |
| const noexcept -> bool |
| { |
| return !has_value(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::value() |
| const & -> void |
| { |
| static_cast<void>( |
| has_value() || |
| (detail::throw_bad_result_access(m_storage.storage.m_error), true) |
| ); |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::value() |
| && -> void |
| { |
| static_cast<void>( |
| has_value() || |
| (detail::throw_bad_result_access(static_cast<E&&>(m_storage.storage.m_error)), true) |
| ); |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::error() |
| const & |
| noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E |
| { |
| return has_value() ? E{} : m_storage.storage.m_error; |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::error() |
| && noexcept(std::is_nothrow_constructible<E>::value && |
| std::is_nothrow_copy_constructible<E>::value) -> E |
| { |
| return has_value() ? E{} : static_cast<E&&>(m_storage.storage.m_error); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename String, typename> |
| inline RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void,E>::expect(String&& message) |
| const & -> void |
| { |
| if (has_error()) { |
| detail::throw_bad_result_access_message( |
| detail::forward<String>(message), |
| m_storage.storage.m_error |
| ); |
| } |
| } |
| |
| template <typename E> |
| template <typename String, typename> |
| inline RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void,E>::expect(String&& message) |
| && -> void |
| { |
| if (has_error()) { |
| detail::throw_bad_result_access_message( |
| detail::forward<String>(message), |
| static_cast<E&&>(m_storage.storage.m_error) |
| ); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Monadic Functionalities |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::error_or(U&& default_error) |
| const & -> error_type |
| { |
| return has_value() |
| ? detail::forward<U>(default_error) |
| : m_storage.storage.m_error; |
| } |
| |
| template <typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::error_or(U&& default_error) |
| && -> error_type |
| { |
| return has_value() |
| ? detail::forward<U>(default_error) |
| : static_cast<E&&>(m_storage.storage.m_error); |
| } |
| |
| template <typename E> |
| template <typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::and_then(U&& value) |
| const -> result<typename std::decay<U>::type,E> |
| { |
| return map([&value]{ |
| return detail::forward<U>(value); |
| }); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::flat_map(Fn&& fn) |
| const & -> detail::invoke_result_t<Fn> |
| { |
| using result_type = detail::invoke_result_t<Fn>; |
| |
| static_assert( |
| is_result<result_type>::value, |
| "flat_map must return a result type or the program is ill-formed" |
| ); |
| |
| return has_value() |
| ? detail::invoke(detail::forward<Fn>(fn)) |
| : result_type(in_place_error, m_storage.storage.m_error); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::flat_map(Fn&& fn) |
| && -> detail::invoke_result_t<Fn> |
| { |
| using result_type = detail::invoke_result_t<Fn>; |
| |
| static_assert( |
| is_result<result_type>::value, |
| "flat_map must return a result type or the program is ill-formed" |
| ); |
| |
| return has_value() |
| ? detail::invoke(detail::forward<Fn>(fn)) |
| : result_type(in_place_error, static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::map(Fn&& fn) |
| const & -> result<detail::invoke_result_t<Fn>,E> |
| { |
| using result_type = detail::invoke_result_t<Fn>; |
| |
| return map_impl(std::is_void<result_type>{}, detail::forward<Fn>(fn)); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::map(Fn&& fn) |
| && -> result<detail::invoke_result_t<Fn>,E> |
| { |
| using result_type = detail::invoke_result_t<Fn>; |
| |
| return static_cast<result<void,E>&&>(*this).map_impl( |
| std::is_void<result_type>{}, |
| detail::forward<Fn>(fn) |
| ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::map_error(Fn&& fn) |
| const & -> result<void, detail::invoke_result_t<Fn,const E&>> |
| { |
| using result_type = result<void, detail::invoke_result_t<Fn, const E&>>; |
| |
| return has_value() |
| ? result_type{} |
| : result_type(in_place_error, detail::invoke( |
| detail::forward<Fn>(fn), m_storage.storage.m_error |
| )); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::map_error(Fn&& fn) |
| && -> result<void, detail::invoke_result_t<Fn,E&&>> |
| { |
| using result_type = result<void, detail::invoke_result_t<Fn, E&&>>; |
| |
| return has_value() |
| ? result_type{} |
| : result_type(in_place_error, |
| detail::invoke(detail::forward<Fn>(fn), static_cast<E&&>(m_storage.storage.m_error) |
| )); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::flat_map_error(Fn&& fn) |
| const & -> detail::invoke_result_t<Fn,const E&> |
| { |
| using result_type = detail::invoke_result_t<Fn,const E&>; |
| |
| static_assert( |
| is_result<result_type>::value, |
| "flat_map_error must return a result type or the program is ill-formed" |
| ); |
| static_assert( |
| std::is_default_constructible<typename result_type::value_type>::value, |
| "flat_map_error for result<void,E> requires the new T type to be default-" |
| "constructible" |
| ); |
| |
| return has_value() |
| ? result_type{} |
| : detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_error); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::flat_map_error(Fn&& fn) |
| && -> detail::invoke_result_t<Fn,E&&> |
| { |
| using result_type = detail::invoke_result_t<Fn,E&&>; |
| |
| static_assert( |
| is_result<result_type>::value, |
| "flat_map_error must return a result type or the program is ill-formed" |
| ); |
| static_assert( |
| std::is_default_constructible<typename result_type::value_type>::value, |
| "flat_map_error for result<void,E> requires the new T type to be default-" |
| "constructible" |
| ); |
| |
| return has_value() |
| ? result_type{} |
| : detail::invoke(detail::forward<Fn>(fn), static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Private Monadic Functions |
| //----------------------------------------------------------------------------- |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::map_impl(std::true_type, Fn&& fn) |
| const & -> result<void,E> |
| { |
| using result_type = result<void, E>; |
| |
| return has_value() |
| ? (detail::invoke(detail::forward<Fn>(fn)), result_type{}) |
| : result_type(in_place_error, m_storage.storage.m_error); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::result<void, E>::map_impl(std::false_type, Fn&& fn) |
| const & -> result<detail::invoke_result_t<Fn>,E> |
| { |
| using invoke_result_type = detail::invoke_result_t<Fn>; |
| using result_type = result<invoke_result_type, E>; |
| |
| return has_value() |
| ? result_type(in_place, detail::invoke(detail::forward<Fn>(fn))) |
| : result_type(in_place_error, m_storage.storage.m_error); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::map_impl(std::true_type, Fn&& fn) |
| && -> result<void,E> |
| { |
| using result_type = result<void, E>; |
| |
| return has_value() |
| ? (detail::invoke(detail::forward<Fn>(fn)), result_type{}) |
| : result_type(in_place_error, static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| template <typename E> |
| template <typename Fn> |
| inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR |
| auto RESULT_NS_IMPL::result<void, E>::map_impl(std::false_type, Fn&& fn) |
| && -> result<detail::invoke_result_t<Fn>,E> |
| { |
| using invoke_result_type = detail::invoke_result_t<Fn>; |
| using result_type = result<invoke_result_type, E>; |
| |
| return has_value() |
| ? result_type(in_place, detail::invoke(detail::forward<Fn>(fn))) |
| : result_type(in_place_error, static_cast<E&&>(m_storage.storage.m_error)); |
| } |
| |
| //============================================================================= |
| // non-member functions : class : result |
| //============================================================================= |
| |
| //----------------------------------------------------------------------------- |
| // Comparison |
| //----------------------------------------------------------------------------- |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator==(const result<T1,E1>& lhs, |
| const result<T2,E2>& 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 <typename T1, typename E1, typename T2, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator!=(const result<T1,E1>& lhs, |
| const result<T2,E2>& 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 <typename T1, typename E1, typename T2, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>=(const result<T1,E1>& lhs, |
| const result<T2,E2>& 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<int>(static_cast<bool>(lhs)) >= static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<=(const result<T1,E1>& lhs, |
| const result<T2,E2>& 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<int>(static_cast<bool>(lhs)) <= static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>(const result<T1,E1>& lhs, |
| const result<T2,E2>& 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<int>(static_cast<bool>(lhs)) > static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| template <typename T1, typename E1, typename T2, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<(const result<T1,E1>& lhs, |
| const result<T2,E2>& 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<int>(static_cast<bool>(lhs)) < static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator==(const result<void,E1>& lhs, |
| const result<void,E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.has_value() == rhs.has_value() |
| ? ( |
| lhs.has_value() |
| ? true |
| : detail::extract_error(lhs) == detail::extract_error(rhs) |
| ) |
| : false; |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator!=(const result<void,E1>& lhs, |
| const result<void,E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.has_value() == rhs.has_value() |
| ? ( |
| lhs.has_value() |
| ? false |
| : detail::extract_error(lhs) != detail::extract_error(rhs) |
| ) |
| : true; |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>=(const result<void,E1>& lhs, |
| const result<void,E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.has_value() == rhs.has_value() |
| ? ( |
| lhs.has_value() |
| ? true |
| : detail::extract_error(lhs) >= detail::extract_error(rhs) |
| ) |
| : static_cast<int>(static_cast<bool>(lhs)) >= static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<=(const result<void,E1>& lhs, |
| const result<void,E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.has_value() == rhs.has_value() |
| ? ( |
| lhs.has_value() |
| ? true |
| : detail::extract_error(lhs) <= detail::extract_error(rhs) |
| ) |
| : static_cast<int>(static_cast<bool>(lhs)) <= static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>(const result<void,E1>& lhs, |
| const result<void,E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.has_value() == rhs.has_value() |
| ? ( |
| lhs.has_value() |
| ? false |
| : detail::extract_error(lhs) > detail::extract_error(rhs) |
| ) |
| : static_cast<int>(static_cast<bool>(lhs)) > static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| template <typename E1, typename E2> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<(const result<void,E1>& lhs, |
| const result<void,E2>& rhs) |
| noexcept -> bool |
| { |
| return lhs.has_value() == rhs.has_value() |
| ? ( |
| lhs.has_value() |
| ? false |
| : detail::extract_error(lhs) < detail::extract_error(rhs) |
| ) |
| : static_cast<int>(static_cast<bool>(lhs)) < static_cast<int>(static_cast<bool>(rhs)); |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E, typename U, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator==(const result<T,E>& exp, const U& value) |
| noexcept -> bool |
| { |
| return (exp.has_value() && *exp == value); |
| } |
| |
| template <typename T, typename U, typename E, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator==(const T& value, const result<U,E>& exp) |
| noexcept -> bool |
| { |
| return (exp.has_value() && *exp == value); |
| } |
| |
| template <typename T, typename E, typename U, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator!=(const result<T,E>& exp, const U& value) |
| noexcept -> bool |
| { |
| return exp.has_value() ? *exp != value : true; |
| } |
| |
| template <typename T, typename U, typename E, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator!=(const T& value, const result<U,E>& exp) |
| noexcept -> bool |
| { |
| return exp.has_value() ? value != *exp : true; |
| } |
| |
| template <typename T, typename E, typename U, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<=(const result<T,E>& exp, const U& value) |
| noexcept -> bool |
| { |
| return exp.has_value() ? *exp <= value : false; |
| } |
| |
| template <typename T, typename U, typename E, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<=(const T& value, const result<U,E>& exp) |
| noexcept -> bool |
| { |
| return exp.has_value() ? value <= *exp : true; |
| } |
| |
| template <typename T, typename E, typename U, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>=(const result<T,E>& exp, const U& value) |
| noexcept -> bool |
| { |
| return exp.has_value() ? *exp >= value : true; |
| } |
| |
| template <typename T, typename U, typename E, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>=(const T& value, const result<U,E>& exp) |
| noexcept -> bool |
| { |
| return exp.has_value() ? value >= *exp : false; |
| } |
| |
| template <typename T, typename E, typename U, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<(const result<T,E>& exp, const U& value) |
| noexcept -> bool |
| { |
| return exp.has_value() ? *exp < value : false; |
| } |
| |
| template <typename T, typename U, typename E, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<(const T& value, const result<U,E>& exp) |
| noexcept -> bool |
| { |
| return exp.has_value() ? value < *exp : true; |
| } |
| |
| template <typename T, typename E, typename U, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>(const result<T,E>& exp, const U& value) |
| noexcept -> bool |
| { |
| return exp.has_value() ? *exp > value : false; |
| } |
| |
| template <typename T, typename U, typename E, typename> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>(const T& value, const result<U,E>& exp) |
| noexcept -> bool |
| { |
| return exp.has_value() ? value > *exp : true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E, typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator==(const result<T,E>& exp, const failure<U>& error) |
| noexcept -> bool |
| { |
| return exp.has_error() ? detail::extract_error(exp) == error.error() : false; |
| } |
| |
| template <typename T, typename U, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator==(const failure<T>& error, const result<E,U>& exp) |
| noexcept -> bool |
| { |
| return exp.has_error() ? error.error() == detail::extract_error(exp) : false; |
| } |
| |
| template <typename T, typename E, typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator!=(const result<T,E>& exp, const failure<U>& error) |
| noexcept -> bool |
| { |
| return exp.has_error() ? detail::extract_error(exp) != error.error() : true; |
| } |
| |
| template <typename T, typename U, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator!=(const failure<T>& error, const result<E,U>& exp) |
| noexcept -> bool |
| { |
| return exp.has_error() ? error.error() != detail::extract_error(exp) : true; |
| } |
| |
| template <typename T, typename E, typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<=(const result<T,E>& exp, const failure<U>& error) |
| noexcept -> bool |
| { |
| return exp.has_error() ? detail::extract_error(exp) <= error.error() : true; |
| } |
| |
| template <typename T, typename U, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<=(const failure<T>& error, const result<E,U>& exp) |
| noexcept -> bool |
| { |
| return exp.has_error() ? error.error() <= detail::extract_error(exp) : false; |
| } |
| |
| template <typename T, typename E, typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>=(const result<T,E>& exp, const failure<U>& error) |
| noexcept -> bool |
| { |
| return exp.has_error() ? detail::extract_error(exp) >= error.error() : false; |
| } |
| |
| template <typename T, typename U, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>=(const failure<T>& error, const result<E,U>& exp) |
| noexcept -> bool |
| { |
| return exp.has_error() ? error.error() >= detail::extract_error(exp) : true; |
| } |
| |
| template <typename T, typename E, typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<(const result<T,E>& exp, const failure<U>& error) |
| noexcept -> bool |
| { |
| return exp.has_error() ? detail::extract_error(exp) < error.error() : true; |
| } |
| |
| template <typename T, typename U, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator<(const failure<T>& error, const result<E,U>& exp) |
| noexcept -> bool |
| { |
| return exp.has_error() ? error.error() < detail::extract_error(exp) : false; |
| } |
| |
| template <typename T, typename E, typename U> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>(const result<T,E>& exp, const failure<U>& error) |
| noexcept -> bool |
| { |
| return exp.has_error() ? detail::extract_error(exp) > error.error() : false; |
| } |
| |
| template <typename T, typename U, typename E> |
| inline RESULT_INLINE_VISIBILITY constexpr |
| auto RESULT_NS_IMPL::operator>(const failure<T>& error, const result<E,U>& exp) |
| noexcept -> bool |
| { |
| return exp.has_error() ? error.error() > detail::extract_error(exp) : true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Utilities |
| //----------------------------------------------------------------------------- |
| |
| template <typename T, typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::swap(result<T,E>& lhs, result<T,E>& rhs) |
| #if __cplusplus >= 201703L |
| noexcept(std::is_nothrow_move_constructible<result<T,E>>::value && |
| std::is_nothrow_move_assignable<result<T,E>>::value && |
| std::is_nothrow_swappable<T>::value && |
| std::is_nothrow_swappable<E>::value) |
| #else |
| noexcept(std::is_nothrow_move_constructible<result<T,E>>::value && |
| std::is_nothrow_move_assignable<result<T,E>>::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<result<T,E>&&>(lhs); |
| lhs = static_cast<result<T,E>&&>(rhs); |
| rhs = static_cast<result<T,E>&&>(temp); |
| } |
| } |
| |
| template <typename E> |
| inline RESULT_INLINE_VISIBILITY |
| auto RESULT_NS_IMPL::swap(result<void,E>& lhs, result<void,E>& rhs) |
| #if __cplusplus >= 201703L |
| noexcept(std::is_nothrow_move_constructible<result<void,E>>::value && |
| std::is_nothrow_move_assignable<result<void,E>>::value && |
| std::is_nothrow_swappable<E>::value) |
| #else |
| noexcept(std::is_nothrow_move_constructible<result<void,E>>::value && |
| std::is_nothrow_move_assignable<result<void,E>>::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<result<void,E>&&>(lhs); |
| lhs = static_cast<result<void,E>&&>(rhs); |
| rhs = static_cast<result<void,E>&&>(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 */ |