// This version targets C++11 and later. // // Copyright (C) 2016-2020 Martin Moene. // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // expected lite is based on: // A proposal to add a utility class to represent expected monad // by Vicente J. Botet Escriba and Pierre Talbot. http:://wg21.link/p0323 #ifndef NONSTD_EXPECTED_LITE_HPP #define NONSTD_EXPECTED_LITE_HPP #define expected_lite_MAJOR 0 #define expected_lite_MINOR 6 #define expected_lite_PATCH 2 #define expected_lite_VERSION expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY(expected_lite_MINOR) "." expected_STRINGIFY(expected_lite_PATCH) #define expected_STRINGIFY( x ) expected_STRINGIFY_( x ) #define expected_STRINGIFY_( x ) #x // expected-lite configuration: #define nsel_EXPECTED_DEFAULT 0 #define nsel_EXPECTED_NONSTD 1 #define nsel_EXPECTED_STD 2 // tweak header support: #ifdef __has_include # if __has_include() # include # endif #define expected_HAVE_TWEAK_HEADER 1 #else #define expected_HAVE_TWEAK_HEADER 0 //# pragma message("expected.hpp: Note: Tweak header not supported.") #endif // expected selection and configuration: #if !defined( nsel_CONFIG_SELECT_EXPECTED ) # define nsel_CONFIG_SELECT_EXPECTED ( nsel_HAVE_STD_EXPECTED ? nsel_EXPECTED_STD : nsel_EXPECTED_NONSTD ) #endif // Proposal revisions: // // DXXXXR0: -- // N4015 : -2 (2014-05-26) // N4109 : -1 (2014-06-29) // P0323R0: 0 (2016-05-28) // P0323R1: 1 (2016-10-12) // -------: // P0323R2: 2 (2017-06-15) // P0323R3: 3 (2017-10-15) // P0323R4: 4 (2017-11-26) // P0323R5: 5 (2018-02-08) // P0323R6: 6 (2018-04-02) // P0323R7: 7 (2018-06-22) * // // expected-lite uses 2 and higher #ifndef nsel_P0323R # define nsel_P0323R 7 #endif // Control presence of C++ exception handling (try and auto discover): #ifndef nsel_CONFIG_NO_EXCEPTIONS # if defined(_MSC_VER) # include // for _HAS_EXCEPTIONS # endif # if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) # define nsel_CONFIG_NO_EXCEPTIONS 0 # else # define nsel_CONFIG_NO_EXCEPTIONS 1 # endif #endif // at default use SEH with MSVC for no C++ exceptions #ifndef nsel_CONFIG_NO_EXCEPTIONS_SEH # define nsel_CONFIG_NO_EXCEPTIONS_SEH ( nsel_CONFIG_NO_EXCEPTIONS && _MSC_VER ) #endif // C++ language version detection (C++23 is speculative): // Note: VC14.0/1900 (VS2015) lacks too much from C++14. #ifndef nsel_CPLUSPLUS # if defined(_MSVC_LANG ) && !defined(__clang__) # define nsel_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) # else # define nsel_CPLUSPLUS __cplusplus # endif #endif #define nsel_CPP98_OR_GREATER ( nsel_CPLUSPLUS >= 199711L ) #define nsel_CPP11_OR_GREATER ( nsel_CPLUSPLUS >= 201103L ) #define nsel_CPP14_OR_GREATER ( nsel_CPLUSPLUS >= 201402L ) #define nsel_CPP17_OR_GREATER ( nsel_CPLUSPLUS >= 201703L ) #define nsel_CPP20_OR_GREATER ( nsel_CPLUSPLUS >= 202002L ) #define nsel_CPP23_OR_GREATER ( nsel_CPLUSPLUS >= 202300L ) // Use C++23 std::expected if available and requested: #if nsel_CPP23_OR_GREATER && defined(__has_include ) # if __has_include( ) # define nsel_HAVE_STD_EXPECTED 1 # else # define nsel_HAVE_STD_EXPECTED 0 # endif #else # define nsel_HAVE_STD_EXPECTED 0 #endif #define nsel_USES_STD_EXPECTED ( (nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_STD) || ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_DEFAULT) && nsel_HAVE_STD_EXPECTED) ) // // in_place: code duplicated in any-lite, expected-lite, expected-lite, value-ptr-lite, variant-lite: // #ifndef nonstd_lite_HAVE_IN_PLACE_TYPES #define nonstd_lite_HAVE_IN_PLACE_TYPES 1 // C++17 std::in_place in : #if nsel_CPP17_OR_GREATER #include namespace nonstd { using std::in_place; using std::in_place_type; using std::in_place_index; using std::in_place_t; using std::in_place_type_t; using std::in_place_index_t; #define nonstd_lite_in_place_t( T) std::in_place_t #define nonstd_lite_in_place_type_t( T) std::in_place_type_t #define nonstd_lite_in_place_index_t(K) std::in_place_index_t #define nonstd_lite_in_place( T) std::in_place_t{} #define nonstd_lite_in_place_type( T) std::in_place_type_t{} #define nonstd_lite_in_place_index(K) std::in_place_index_t{} } // namespace nonstd #else // nsel_CPP17_OR_GREATER #include namespace nonstd { namespace detail { template< class T > struct in_place_type_tag {}; template< std::size_t K > struct in_place_index_tag {}; } // namespace detail struct in_place_t {}; template< class T > inline in_place_t in_place( detail::in_place_type_tag = detail::in_place_type_tag() ) { return in_place_t(); } template< std::size_t K > inline in_place_t in_place( detail::in_place_index_tag = detail::in_place_index_tag() ) { return in_place_t(); } template< class T > inline in_place_t in_place_type( detail::in_place_type_tag = detail::in_place_type_tag() ) { return in_place_t(); } template< std::size_t K > inline in_place_t in_place_index( detail::in_place_index_tag = detail::in_place_index_tag() ) { return in_place_t(); } // mimic templated typedef: #define nonstd_lite_in_place_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag ) #define nonstd_lite_in_place_type_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag ) #define nonstd_lite_in_place_index_t(K) nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag ) #define nonstd_lite_in_place( T) nonstd::in_place_type #define nonstd_lite_in_place_type( T) nonstd::in_place_type #define nonstd_lite_in_place_index(K) nonstd::in_place_index } // namespace nonstd #endif // nsel_CPP17_OR_GREATER #endif // nonstd_lite_HAVE_IN_PLACE_TYPES // // Using std::expected: // #if nsel_USES_STD_EXPECTED #include namespace nonstd { using std::expected; // ... } #else // nsel_USES_STD_EXPECTED #include #include #include #include #include #include #include #include #include // additional includes: #if nsel_CONFIG_NO_EXCEPTIONS # if nsel_CONFIG_NO_EXCEPTIONS_SEH # include // for ExceptionCodes # else // already included: # endif #else # include #endif // C++ feature usage: #if nsel_CPP11_OR_GREATER # define nsel_constexpr constexpr #else # define nsel_constexpr /*constexpr*/ #endif #if nsel_CPP14_OR_GREATER # define nsel_constexpr14 constexpr #else # define nsel_constexpr14 /*constexpr*/ #endif #if nsel_CPP17_OR_GREATER # define nsel_inline17 inline #else # define nsel_inline17 /*inline*/ #endif // Compiler versions: // // MSVC++ 6.0 _MSC_VER == 1200 nsel_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) // MSVC++ 7.0 _MSC_VER == 1300 nsel_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) // MSVC++ 7.1 _MSC_VER == 1310 nsel_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) // MSVC++ 8.0 _MSC_VER == 1400 nsel_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) // MSVC++ 9.0 _MSC_VER == 1500 nsel_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) // MSVC++ 10.0 _MSC_VER == 1600 nsel_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) // MSVC++ 11.0 _MSC_VER == 1700 nsel_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) // MSVC++ 12.0 _MSC_VER == 1800 nsel_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) // MSVC++ 14.0 _MSC_VER == 1900 nsel_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) // MSVC++ 14.1 _MSC_VER >= 1910 nsel_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) // MSVC++ 14.2 _MSC_VER >= 1920 nsel_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) #if defined(_MSC_VER) && !defined(__clang__) # define nsel_COMPILER_MSVC_VER (_MSC_VER ) # define nsel_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900)) ) #else # define nsel_COMPILER_MSVC_VER 0 # define nsel_COMPILER_MSVC_VERSION 0 #endif #define nsel_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) #if defined(__clang__) # define nsel_COMPILER_CLANG_VERSION nsel_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) #else # define nsel_COMPILER_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) # define nsel_COMPILER_GNUC_VERSION nsel_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #else # define nsel_COMPILER_GNUC_VERSION 0 #endif // half-open range [lo..hi): //#define nsel_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) // Method enabling #define nsel_REQUIRES_0(...) \ template< bool B = (__VA_ARGS__), typename std::enable_if::type = 0 > #define nsel_REQUIRES_T(...) \ , typename std::enable_if< (__VA_ARGS__), int >::type = 0 #define nsel_REQUIRES_R(R, ...) \ typename std::enable_if< (__VA_ARGS__), R>::type #define nsel_REQUIRES_A(...) \ , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr // Presence of language and library features: #ifdef _HAS_CPP0X # define nsel_HAS_CPP0X _HAS_CPP0X #else # define nsel_HAS_CPP0X 0 #endif //#define nsel_CPP11_140 (nsel_CPP11_OR_GREATER || nsel_COMPILER_MSVC_VER >= 1900) // Clang, GNUC, MSVC warning suppression macros: #ifdef __clang__ # pragma clang diagnostic push #elif defined __GNUC__ # pragma GCC diagnostic push #endif // __clang__ #if nsel_COMPILER_MSVC_VERSION >= 140 # pragma warning( push ) # define nsel_DISABLE_MSVC_WARNINGS(codes) __pragma( warning(disable: codes) ) #else # define nsel_DISABLE_MSVC_WARNINGS(codes) #endif #ifdef __clang__ # define nsel_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") #elif defined __GNUC__ # define nsel_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") #elif nsel_COMPILER_MSVC_VERSION >= 140 # define nsel_RESTORE_WARNINGS() __pragma( warning( pop ) ) #else # define nsel_RESTORE_WARNINGS() #endif // Suppress the following MSVC (GSL) warnings: // - C26409: Avoid calling new and delete explicitly, use std::make_unique instead (r.11) nsel_DISABLE_MSVC_WARNINGS( 26409 ) // // expected: // namespace nonstd { namespace expected_lite { // type traits C++17: namespace std17 { #if nsel_CPP17_OR_GREATER using std::conjunction; using std::is_swappable; using std::is_nothrow_swappable; #else // nsel_CPP17_OR_GREATER namespace detail { using std::swap; struct is_swappable { template< typename T, typename = decltype( swap( std::declval(), std::declval() ) ) > static std::true_type test( int /* unused */); template< typename > static std::false_type test(...); }; struct is_nothrow_swappable { // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015): template< typename T > static constexpr bool satisfies() { return noexcept( swap( std::declval(), std::declval() ) ); } template< typename T > static auto test( int ) -> std::integral_constant()>{} template< typename > static auto test(...) -> std::false_type; }; } // namespace detail // is [nothow] swappable: template< typename T > struct is_swappable : decltype( detail::is_swappable::test(0) ){}; template< typename T > struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test(0) ){}; // conjunction: template< typename... > struct conjunction : std::true_type{}; template< typename B1 > struct conjunction : B1{}; template< typename B1, typename... Bn > struct conjunction : std::conditional, B1>::type{}; #endif // nsel_CPP17_OR_GREATER } // namespace std17 // type traits C++20: namespace std20 { #if defined(__cpp_lib_remove_cvref) using std::remove_cvref; #else template< typename T > struct remove_cvref { typedef typename std::remove_cv< typename std::remove_reference::type >::type type; }; #endif } // namespace std20 // forward declaration: template< typename T, typename E > class expected; namespace detail { /// discriminated union to hold value or 'error'. template< typename T, typename E > class storage_t_impl { template< typename, typename > friend class nonstd::expected_lite::expected; public: using value_type = T; using error_type = E; // no-op construction storage_t_impl() {} ~storage_t_impl() {} explicit storage_t_impl( bool has_value ) : m_has_value( has_value ) {} void construct_value( value_type const & e ) { new( &m_value ) value_type( e ); } void construct_value( value_type && e ) { new( &m_value ) value_type( std::move( e ) ); } template< class... Args > void emplace_value( Args&&... args ) { new( &m_value ) value_type( std::forward(args)...); } template< class U, class... Args > void emplace_value( std::initializer_list il, Args&&... args ) { new( &m_value ) value_type( il, std::forward(args)... ); } void destruct_value() { m_value.~value_type(); } void construct_error( error_type const & e ) { new( &m_error ) error_type( e ); } void construct_error( error_type && e ) { new( &m_error ) error_type( std::move( e ) ); } template< class... Args > void emplace_error( Args&&... args ) { new( &m_error ) error_type( std::forward(args)...); } template< class U, class... Args > void emplace_error( std::initializer_list il, Args&&... args ) { new( &m_error ) error_type( il, std::forward(args)... ); } void destruct_error() { m_error.~error_type(); } constexpr value_type const & value() const & { return m_value; } value_type & value() & { return m_value; } constexpr value_type const && value() const && { return std::move( m_value ); } nsel_constexpr14 value_type && value() && { return std::move( m_value ); } value_type const * value_ptr() const { return &m_value; } value_type * value_ptr() { return &m_value; } error_type const & error() const & { return m_error; } error_type & error() & { return m_error; } constexpr error_type const && error() const && { return std::move( m_error ); } nsel_constexpr14 error_type && error() && { return std::move( m_error ); } bool has_value() const { return m_has_value; } void set_has_value( bool v ) { m_has_value = v; } private: union { value_type m_value; error_type m_error; }; bool m_has_value = false; }; /// discriminated union to hold only 'error'. template< typename E > struct storage_t_impl { template< typename, typename > friend class nonstd::expected_lite::expected; public: using value_type = void; using error_type = E; // no-op construction storage_t_impl() {} ~storage_t_impl() {} explicit storage_t_impl( bool has_value ) : m_has_value( has_value ) {} void construct_error( error_type const & e ) { new( &m_error ) error_type( e ); } void construct_error( error_type && e ) { new( &m_error ) error_type( std::move( e ) ); } template< class... Args > void emplace_error( Args&&... args ) { new( &m_error ) error_type( std::forward(args)...); } template< class U, class... Args > void emplace_error( std::initializer_list il, Args&&... args ) { new( &m_error ) error_type( il, std::forward(args)... ); } void destruct_error() { m_error.~error_type(); } error_type const & error() const & { return m_error; } error_type & error() & { return m_error; } constexpr error_type const && error() const && { return std::move( m_error ); } nsel_constexpr14 error_type && error() && { return std::move( m_error ); } bool has_value() const { return m_has_value; } void set_has_value( bool v ) { m_has_value = v; } private: union { char m_dummy; error_type m_error; }; bool m_has_value = false; }; template< typename T, typename E, bool isConstructable, bool isMoveable > class storage_t { public: storage_t() = default; ~storage_t() = default; explicit storage_t( bool has_value ) : storage_t_impl( has_value ) {} storage_t( storage_t const & other ) = delete; storage_t( storage_t && other ) = delete; }; template< typename T, typename E > class storage_t : public storage_t_impl { public: storage_t() = default; ~storage_t() = default; explicit storage_t( bool has_value ) : storage_t_impl( has_value ) {} storage_t( storage_t const & other ) : storage_t_impl( other.has_value() ) { if ( this->has_value() ) this->construct_value( other.value() ); else this->construct_error( other.error() ); } storage_t(storage_t && other ) : storage_t_impl( other.has_value() ) { if ( this->has_value() ) this->construct_value( std::move( other.value() ) ); else this->construct_error( std::move( other.error() ) ); } }; template< typename E > class storage_t : public storage_t_impl { public: storage_t() = default; ~storage_t() = default; explicit storage_t( bool has_value ) : storage_t_impl( has_value ) {} storage_t( storage_t const & other ) : storage_t_impl( other.has_value() ) { if ( this->has_value() ) ; else this->construct_error( other.error() ); } storage_t(storage_t && other ) : storage_t_impl( other.has_value() ) { if ( this->has_value() ) ; else this->construct_error( std::move( other.error() ) ); } }; template< typename T, typename E > class storage_t : public storage_t_impl { public: storage_t() = default; ~storage_t() = default; explicit storage_t( bool has_value ) : storage_t_impl( has_value ) {} storage_t( storage_t const & other ) : storage_t_impl(other.has_value()) { if ( this->has_value() ) this->construct_value( other.value() ); else this->construct_error( other.error() ); } storage_t( storage_t && other ) = delete; }; template< typename E > class storage_t : public storage_t_impl { public: storage_t() = default; ~storage_t() = default; explicit storage_t( bool has_value ) : storage_t_impl( has_value ) {} storage_t( storage_t const & other ) : storage_t_impl(other.has_value()) { if ( this->has_value() ) ; else this->construct_error( other.error() ); } storage_t( storage_t && other ) = delete; }; template< typename T, typename E > class storage_t : public storage_t_impl { public: storage_t() = default; ~storage_t() = default; explicit storage_t( bool has_value ) : storage_t_impl( has_value ) {} storage_t( storage_t const & other ) = delete; storage_t( storage_t && other ) : storage_t_impl( other.has_value() ) { if ( this->has_value() ) this->construct_value( std::move( other.value() ) ); else this->construct_error( std::move( other.error() ) ); } }; template< typename E > class storage_t : public storage_t_impl { public: storage_t() = default; ~storage_t() = default; explicit storage_t( bool has_value ) : storage_t_impl( has_value ) {} storage_t( storage_t const & other ) = delete; storage_t( storage_t && other ) : storage_t_impl( other.has_value() ) { if ( this->has_value() ) ; else this->construct_error( std::move( other.error() ) ); } }; } // namespace detail /// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also use aliased type unexpected. #if nsel_P0323R <= 2 template< typename E = std::exception_ptr > class unexpected_type #else template< typename E > class unexpected_type #endif // nsel_P0323R { public: using error_type = E; // x.x.5.2.1 Constructors // unexpected_type() = delete; constexpr unexpected_type( unexpected_type const & ) = default; constexpr unexpected_type( unexpected_type && ) = default; template< typename... Args nsel_REQUIRES_T( std::is_constructible::value ) > constexpr explicit unexpected_type( nonstd_lite_in_place_t(E), Args &&... args ) : m_error( std::forward( args )...) {} template< typename U, typename... Args nsel_REQUIRES_T( std::is_constructible, Args&&...>::value ) > constexpr explicit unexpected_type( nonstd_lite_in_place_t(E), std::initializer_list il, Args &&... args ) : m_error( il, std::forward( args )...) {} template< typename E2 nsel_REQUIRES_T( std::is_constructible::value && !std::is_same< typename std20::remove_cvref::type, nonstd_lite_in_place_t(E2) >::value && !std::is_same< typename std20::remove_cvref::type, unexpected_type >::value ) > constexpr explicit unexpected_type( E2 && error ) : m_error( std::forward( error ) ) {} template< typename E2 nsel_REQUIRES_T( std::is_constructible< E, E2>::value && !std::is_constructible & >::value && !std::is_constructible >::value && !std::is_constructible const & >::value && !std::is_constructible const >::value && !std::is_convertible< unexpected_type &, E>::value && !std::is_convertible< unexpected_type , E>::value && !std::is_convertible< unexpected_type const &, E>::value && !std::is_convertible< unexpected_type const , E>::value && !std::is_convertible< E2 const &, E>::value /*=> explicit */ ) > constexpr explicit unexpected_type( unexpected_type const & error ) : m_error( E{ error.value() } ) {} template< typename E2 nsel_REQUIRES_T( std::is_constructible< E, E2>::value && !std::is_constructible & >::value && !std::is_constructible >::value && !std::is_constructible const & >::value && !std::is_constructible const >::value && !std::is_convertible< unexpected_type &, E>::value && !std::is_convertible< unexpected_type , E>::value && !std::is_convertible< unexpected_type const &, E>::value && !std::is_convertible< unexpected_type const , E>::value && std::is_convertible< E2 const &, E>::value /*=> explicit */ ) > constexpr /*non-explicit*/ unexpected_type( unexpected_type const & error ) : m_error( error.value() ) {} template< typename E2 nsel_REQUIRES_T( std::is_constructible< E, E2>::value && !std::is_constructible & >::value && !std::is_constructible >::value && !std::is_constructible const & >::value && !std::is_constructible const >::value && !std::is_convertible< unexpected_type &, E>::value && !std::is_convertible< unexpected_type , E>::value && !std::is_convertible< unexpected_type const &, E>::value && !std::is_convertible< unexpected_type const , E>::value && !std::is_convertible< E2 const &, E>::value /*=> explicit */ ) > constexpr explicit unexpected_type( unexpected_type && error ) : m_error( E{ std::move( error.value() ) } ) {} template< typename E2 nsel_REQUIRES_T( std::is_constructible< E, E2>::value && !std::is_constructible & >::value && !std::is_constructible >::value && !std::is_constructible const & >::value && !std::is_constructible const >::value && !std::is_convertible< unexpected_type &, E>::value && !std::is_convertible< unexpected_type , E>::value && !std::is_convertible< unexpected_type const &, E>::value && !std::is_convertible< unexpected_type const , E>::value && std::is_convertible< E2 const &, E>::value /*=> non-explicit */ ) > constexpr /*non-explicit*/ unexpected_type( unexpected_type && error ) : m_error( std::move( error.value() ) ) {} // x.x.5.2.2 Assignment nsel_constexpr14 unexpected_type& operator=( unexpected_type const & ) = default; nsel_constexpr14 unexpected_type& operator=( unexpected_type && ) = default; template< typename E2 = E > nsel_constexpr14 unexpected_type & operator=( unexpected_type const & other ) { unexpected_type{ other.value() }.swap( *this ); return *this; } template< typename E2 = E > nsel_constexpr14 unexpected_type & operator=( unexpected_type && other ) { unexpected_type{ std::move( other.value() ) }.swap( *this ); return *this; } // x.x.5.2.3 Observers nsel_constexpr14 E & value() & noexcept { return m_error; } constexpr E const & value() const & noexcept { return m_error; } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 nsel_constexpr14 E && value() && noexcept { return std::move( m_error ); } constexpr E const && value() const && noexcept { return std::move( m_error ); } #endif // x.x.5.2.4 Swap template< typename U=E > nsel_REQUIRES_R( void, std17::is_swappable::value ) swap( unexpected_type & other ) noexcept ( std17::is_nothrow_swappable::value ) { using std::swap; swap( m_error, other.m_error ); } // TODO: ??? unexpected_type: in-class friend operator==, != private: error_type m_error; }; #if nsel_CPP17_OR_GREATER /// template deduction guide: template< typename E > unexpected_type( E ) -> unexpected_type< E >; #endif /// class unexpected_type, std::exception_ptr specialization (P0323R2) #if !nsel_CONFIG_NO_EXCEPTIONS #if nsel_P0323R <= 2 // TODO: Should expected be specialized for particular E types such as exception_ptr and how? // See p0323r7 2.1. Ergonomics, http://wg21.link/p0323 template<> class unexpected_type< std::exception_ptr > { public: using error_type = std::exception_ptr; unexpected_type() = delete; ~unexpected_type(){} explicit unexpected_type( std::exception_ptr const & error ) : m_error( error ) {} explicit unexpected_type(std::exception_ptr && error ) : m_error( std::move( error ) ) {} template< typename E > explicit unexpected_type( E error ) : m_error( std::make_exception_ptr( error ) ) {} std::exception_ptr const & value() const { return m_error; } std::exception_ptr & value() { return m_error; } private: std::exception_ptr m_error; }; #endif // nsel_P0323R #endif // !nsel_CONFIG_NO_EXCEPTIONS /// x.x.4, Unexpected equality operators template< typename E1, typename E2 > constexpr bool operator==( unexpected_type const & x, unexpected_type const & y ) { return x.value() == y.value(); } template< typename E1, typename E2 > constexpr bool operator!=( unexpected_type const & x, unexpected_type const & y ) { return ! ( x == y ); } #if nsel_P0323R <= 2 template< typename E > constexpr bool operator<( unexpected_type const & x, unexpected_type const & y ) { return x.value() < y.value(); } template< typename E > constexpr bool operator>( unexpected_type const & x, unexpected_type const & y ) { return ( y < x ); } template< typename E > constexpr bool operator<=( unexpected_type const & x, unexpected_type const & y ) { return ! ( y < x ); } template< typename E > constexpr bool operator>=( unexpected_type const & x, unexpected_type const & y ) { return ! ( x < y ); } #endif // nsel_P0323R /// x.x.5 Specialized algorithms template< typename E nsel_REQUIRES_T( std17::is_swappable::value ) > void swap( unexpected_type & x, unexpected_type & y) noexcept ( noexcept ( x.swap(y) ) ) { x.swap( y ); } #if nsel_P0323R <= 2 // unexpected: relational operators for std::exception_ptr: inline constexpr bool operator<( unexpected_type const & /*x*/, unexpected_type const & /*y*/ ) { return false; } inline constexpr bool operator>( unexpected_type const & /*x*/, unexpected_type const & /*y*/ ) { return false; } inline constexpr bool operator<=( unexpected_type const & x, unexpected_type const & y ) { return ( x == y ); } inline constexpr bool operator>=( unexpected_type const & x, unexpected_type const & y ) { return ( x == y ); } #endif // nsel_P0323R // unexpected: traits #if nsel_P0323R <= 3 template< typename E> struct is_unexpected : std::false_type {}; template< typename E> struct is_unexpected< unexpected_type > : std::true_type {}; #endif // nsel_P0323R // unexpected: factory // keep make_unexpected() removed in p0323r2 for pre-C++17: template< typename E> nsel_constexpr14 auto make_unexpected( E && value ) -> unexpected_type< typename std::decay::type > { return unexpected_type< typename std::decay::type >( std::forward(value) ); } #if nsel_P0323R <= 3 /*nsel_constexpr14*/ auto inline make_unexpected_from_current_exception() -> unexpected_type< std::exception_ptr > { return unexpected_type< std::exception_ptr >( std::current_exception() ); } #endif // nsel_P0323R /// x.x.6, x.x.7 expected access error template< typename E > class bad_expected_access; /// x.x.7 bad_expected_access: expected access error template <> class bad_expected_access< void > : public std::exception { public: explicit bad_expected_access() : std::exception() {} }; /// x.x.6 bad_expected_access: expected access error #if !nsel_CONFIG_NO_EXCEPTIONS template< typename E > class bad_expected_access : public bad_expected_access< void > { public: using error_type = E; explicit bad_expected_access( error_type error ) : m_error( error ) {} virtual char const * what() const noexcept override { return "bad_expected_access"; } nsel_constexpr14 error_type & error() & { return m_error; } constexpr error_type const & error() const & { return m_error; } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 nsel_constexpr14 error_type && error() && { return std::move( m_error ); } constexpr error_type const && error() const && { return std::move( m_error ); } #endif private: error_type m_error; }; #endif // nsel_CONFIG_NO_EXCEPTIONS /// x.x.8 unexpect tag, in_place_unexpected tag: construct an error struct unexpect_t{}; using in_place_unexpected_t = unexpect_t; nsel_inline17 constexpr unexpect_t unexpect{}; nsel_inline17 constexpr unexpect_t in_place_unexpected{}; /// class error_traits #if nsel_CONFIG_NO_EXCEPTIONS namespace detail { inline bool text( char const * /*text*/ ) { return true; } } template< typename Error > struct error_traits { static void rethrow( Error const & /*e*/ ) { #if nsel_CONFIG_NO_EXCEPTIONS_SEH RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL ); #else assert( false && detail::text("throw bad_expected_access{ e };") ); #endif } }; template<> struct error_traits< std::exception_ptr > { static void rethrow( std::exception_ptr const & /*e*/ ) { #if nsel_CONFIG_NO_EXCEPTIONS_SEH RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL ); #else assert( false && detail::text("throw bad_expected_access{ e };") ); #endif } }; template<> struct error_traits< std::error_code > { static void rethrow( std::error_code const & /*e*/ ) { #if nsel_CONFIG_NO_EXCEPTIONS_SEH RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL ); #else assert( false && detail::text("throw std::system_error( e );") ); #endif } }; #else // nsel_CONFIG_NO_EXCEPTIONS template< typename Error > struct error_traits { static void rethrow( Error const & e ) { throw bad_expected_access{ e }; } }; template<> struct error_traits< std::exception_ptr > { static void rethrow( std::exception_ptr const & e ) { std::rethrow_exception( e ); } }; template<> struct error_traits< std::error_code > { static void rethrow( std::error_code const & e ) { throw std::system_error( e ); } }; #endif // nsel_CONFIG_NO_EXCEPTIONS } // namespace expected_lite // provide nonstd::unexpected_type: using expected_lite::unexpected_type; namespace expected_lite { /// class expected #if nsel_P0323R <= 2 template< typename T, typename E = std::exception_ptr > class expected #else template< typename T, typename E > class expected #endif // nsel_P0323R { private: template< typename, typename > friend class expected; public: using value_type = T; using error_type = E; using unexpected_type = nonstd::unexpected_type; template< typename U > struct rebind { using type = expected; }; // x.x.4.1 constructors nsel_REQUIRES_0( std::is_default_constructible::value ) nsel_constexpr14 expected() : contained( true ) { contained.construct_value( value_type() ); } nsel_constexpr14 expected( expected const & ) = default; nsel_constexpr14 expected( expected && ) = default; template< typename U, typename G nsel_REQUIRES_T( std::is_constructible< T, U const &>::value && std::is_constructible::value && !std::is_constructible & >::value && !std::is_constructible && >::value && !std::is_constructible const & >::value && !std::is_constructible const && >::value && !std::is_convertible< expected & , T>::value && !std::is_convertible< expected &&, T>::value && !std::is_convertible< expected const & , T>::value && !std::is_convertible< expected const &&, T>::value && (!std::is_convertible::value || !std::is_convertible::value ) /*=> explicit */ ) > nsel_constexpr14 explicit expected( expected const & other ) : contained( other.has_value() ) { if ( has_value() ) contained.construct_value( T{ other.contained.value() } ); else contained.construct_error( E{ other.contained.error() } ); } template< typename U, typename G nsel_REQUIRES_T( std::is_constructible< T, U const &>::value && std::is_constructible::value && !std::is_constructible & >::value && !std::is_constructible && >::value && !std::is_constructible const & >::value && !std::is_constructible const && >::value && !std::is_convertible< expected & , T>::value && !std::is_convertible< expected &&, T>::value && !std::is_convertible< expected const &, T>::value && !std::is_convertible< expected const &&, T>::value && !(!std::is_convertible::value || !std::is_convertible::value ) /*=> non-explicit */ ) > nsel_constexpr14 /*non-explicit*/ expected( expected const & other ) : contained( other.has_value() ) { if ( has_value() ) contained.construct_value( other.contained.value() ); else contained.construct_error( other.contained.error() ); } template< typename U, typename G nsel_REQUIRES_T( std::is_constructible< T, U>::value && std::is_constructible::value && !std::is_constructible & >::value && !std::is_constructible && >::value && !std::is_constructible const & >::value && !std::is_constructible const && >::value && !std::is_convertible< expected & , T>::value && !std::is_convertible< expected &&, T>::value && !std::is_convertible< expected const & , T>::value && !std::is_convertible< expected const &&, T>::value && (!std::is_convertible::value || !std::is_convertible::value ) /*=> explicit */ ) > nsel_constexpr14 explicit expected( expected && other ) : contained( other.has_value() ) { if ( has_value() ) contained.construct_value( T{ std::move( other.contained.value() ) } ); else contained.construct_error( E{ std::move( other.contained.error() ) } ); } template< typename U, typename G nsel_REQUIRES_T( std::is_constructible< T, U>::value && std::is_constructible::value && !std::is_constructible & >::value && !std::is_constructible && >::value && !std::is_constructible const & >::value && !std::is_constructible const && >::value && !std::is_convertible< expected & , T>::value && !std::is_convertible< expected &&, T>::value && !std::is_convertible< expected const & , T>::value && !std::is_convertible< expected const &&, T>::value && !(!std::is_convertible::value || !std::is_convertible::value ) /*=> non-explicit */ ) > nsel_constexpr14 /*non-explicit*/ expected( expected && other ) : contained( other.has_value() ) { if ( has_value() ) contained.construct_value( std::move( other.contained.value() ) ); else contained.construct_error( std::move( other.contained.error() ) ); } template< typename U = T nsel_REQUIRES_T( std::is_copy_constructible::value ) > nsel_constexpr14 expected( value_type const & value ) : contained( true ) { contained.construct_value( value ); } template< typename U = T nsel_REQUIRES_T( std::is_constructible::value && !std::is_same::type, nonstd_lite_in_place_t(U)>::value && !std::is_same< expected , typename std20::remove_cvref::type>::value && !std::is_same, typename std20::remove_cvref::type>::value && !std::is_convertible::value /*=> explicit */ ) > nsel_constexpr14 explicit expected( U && value ) noexcept ( std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value ) : contained( true ) { contained.construct_value( T{ std::forward( value ) } ); } template< typename U = T nsel_REQUIRES_T( std::is_constructible::value && !std::is_same::type, nonstd_lite_in_place_t(U)>::value && !std::is_same< expected , typename std20::remove_cvref::type>::value && !std::is_same, typename std20::remove_cvref::type>::value && std::is_convertible::value /*=> non-explicit */ ) > nsel_constexpr14 /*non-explicit*/ expected( U && value ) noexcept ( std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value ) : contained( true ) { contained.construct_value( std::forward( value ) ); } // construct error: template< typename G = E nsel_REQUIRES_T( std::is_constructible::value && !std::is_convertible< G const &, E>::value /*=> explicit */ ) > nsel_constexpr14 explicit expected( nonstd::unexpected_type const & error ) : contained( false ) { contained.construct_error( E{ error.value() } ); } template< typename G = E nsel_REQUIRES_T( std::is_constructible::value && std::is_convertible< G const &, E>::value /*=> non-explicit */ ) > nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type const & error ) : contained( false ) { contained.construct_error( error.value() ); } template< typename G = E nsel_REQUIRES_T( std::is_constructible::value && !std::is_convertible< G&&, E>::value /*=> explicit */ ) > nsel_constexpr14 explicit expected( nonstd::unexpected_type && error ) : contained( false ) { contained.construct_error( E{ std::move( error.value() ) } ); } template< typename G = E nsel_REQUIRES_T( std::is_constructible::value && std::is_convertible< G&&, E>::value /*=> non-explicit */ ) > nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type && error ) : contained( false ) { contained.construct_error( std::move( error.value() ) ); } // in-place construction, value template< typename... Args nsel_REQUIRES_T( std::is_constructible::value ) > nsel_constexpr14 explicit expected( nonstd_lite_in_place_t(T), Args&&... args ) : contained( true ) { contained.emplace_value( std::forward( args )... ); } template< typename U, typename... Args nsel_REQUIRES_T( std::is_constructible, Args&&...>::value ) > nsel_constexpr14 explicit expected( nonstd_lite_in_place_t(T), std::initializer_list il, Args&&... args ) : contained( true ) { contained.emplace_value( il, std::forward( args )... ); } // in-place construction, error template< typename... Args nsel_REQUIRES_T( std::is_constructible::value ) > nsel_constexpr14 explicit expected( unexpect_t, Args&&... args ) : contained( false ) { contained.emplace_error( std::forward( args )... ); } template< typename U, typename... Args nsel_REQUIRES_T( std::is_constructible, Args&&...>::value ) > nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list il, Args&&... args ) : contained( false ) { contained.emplace_error( il, std::forward( args )... ); } // x.x.4.2 destructor // TODO: ~expected: triviality // Effects: If T is not cv void and is_trivially_destructible_v is false and bool(*this), calls val.~T(). If is_trivially_destructible_v is false and !bool(*this), calls unexpect.~unexpected(). // Remarks: If either T is cv void or is_trivially_destructible_v is true, and is_trivially_destructible_v is true, then this destructor shall be a trivial destructor. ~expected() { if ( has_value() ) contained.destruct_value(); else contained.destruct_error(); } // x.x.4.3 assignment expected & operator=( expected const & other ) { expected( other ).swap( *this ); return *this; } expected & operator=( expected && other ) noexcept ( std::is_nothrow_move_constructible< T>::value && std::is_nothrow_move_assignable< T>::value && std::is_nothrow_move_constructible::value // added for missing && std::is_nothrow_move_assignable< E>::value ) // nothrow above { expected( std::move( other ) ).swap( *this ); return *this; } template< typename U nsel_REQUIRES_T( !std::is_same, typename std20::remove_cvref::type>::value && std17::conjunction, std::is_same> >::value && std::is_constructible::value && std::is_assignable< T&,U>::value && std::is_nothrow_move_constructible::value ) > expected & operator=( U && value ) { expected( std::forward( value ) ).swap( *this ); return *this; } template< typename G = E nsel_REQUIRES_T( std::is_constructible::value && std::is_copy_constructible::value // TODO: std::is_nothrow_copy_constructible && std::is_copy_assignable::value ) > expected & operator=( nonstd::unexpected_type const & error ) { expected( unexpect, error.value() ).swap( *this ); return *this; } template< typename G = E nsel_REQUIRES_T( std::is_constructible::value && std::is_move_constructible::value // TODO: std::is_nothrow_move_constructible && std::is_move_assignable::value ) > expected & operator=( nonstd::unexpected_type && error ) { expected( unexpect, std::move( error.value() ) ).swap( *this ); return *this; } template< typename... Args nsel_REQUIRES_T( std::is_nothrow_constructible::value ) > value_type & emplace( Args &&... args ) { expected( nonstd_lite_in_place(T), std::forward(args)... ).swap( *this ); return value(); } template< typename U, typename... Args nsel_REQUIRES_T( std::is_nothrow_constructible&, Args&&...>::value ) > value_type & emplace( std::initializer_list il, Args &&... args ) { expected( nonstd_lite_in_place(T), il, std::forward(args)... ).swap( *this ); return value(); } // x.x.4.4 swap template< typename U=T, typename G=E > nsel_REQUIRES_R( void, std17::is_swappable< U>::value && std17::is_swappable::value && ( std::is_move_constructible::value || std::is_move_constructible::value ) ) swap( expected & other ) noexcept ( std::is_nothrow_move_constructible::value && std17::is_nothrow_swappable::value && std::is_nothrow_move_constructible::value && std17::is_nothrow_swappable::value ) { using std::swap; if ( bool(*this) && bool(other) ) { swap( contained.value(), other.contained.value() ); } else if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); } else if ( bool(*this) && ! bool(other) ) { error_type t( std::move( other.error() ) ); other.contained.destruct_error(); other.contained.construct_value( std::move( contained.value() ) ); contained.destruct_value(); contained.construct_error( std::move( t ) ); bool has_value = contained.has_value(); bool other_has_value = other.has_value(); other.contained.set_has_value(has_value); contained.set_has_value(other_has_value); } else if ( ! bool(*this) && bool(other) ) { other.swap( *this ); } } // x.x.4.5 observers constexpr value_type const * operator ->() const { return assert( has_value() ), contained.value_ptr(); } value_type * operator ->() { return assert( has_value() ), contained.value_ptr(); } constexpr value_type const & operator *() const & { return assert( has_value() ), contained.value(); } value_type & operator *() & { return assert( has_value() ), contained.value(); } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 constexpr value_type const && operator *() const && { return std::move( ( assert( has_value() ), contained.value() ) ); } nsel_constexpr14 value_type && operator *() && { return std::move( ( assert( has_value() ), contained.value() ) ); } #endif constexpr explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return contained.has_value(); } constexpr value_type const & value() const & { return has_value() ? ( contained.value() ) : ( error_traits::rethrow( contained.error() ), contained.value() ); } value_type & value() & { return has_value() ? ( contained.value() ) : ( error_traits::rethrow( contained.error() ), contained.value() ); } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 constexpr value_type const && value() const && { return std::move( has_value() ? ( contained.value() ) : ( error_traits::rethrow( contained.error() ), contained.value() ) ); } nsel_constexpr14 value_type && value() && { return std::move( has_value() ? ( contained.value() ) : ( error_traits::rethrow( contained.error() ), contained.value() ) ); } #endif constexpr error_type const & error() const & { return assert( ! has_value() ), contained.error(); } error_type & error() & { return assert( ! has_value() ), contained.error(); } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 constexpr error_type const && error() const && { return std::move( ( assert( ! has_value() ), contained.error() ) ); } error_type && error() && { return std::move( ( assert( ! has_value() ), contained.error() ) ); } #endif constexpr unexpected_type get_unexpected() const { return make_unexpected( contained.error() ); } template< typename Ex > bool has_exception() const { using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type; return ! has_value() && std::is_base_of< Ex, ContainedEx>::value; } template< typename U nsel_REQUIRES_T( std::is_copy_constructible< T>::value && std::is_convertible::value ) > value_type value_or( U && v ) const & { return has_value() ? contained.value() : static_cast( std::forward( v ) ); } template< typename U nsel_REQUIRES_T( std::is_move_constructible< T>::value && std::is_convertible::value ) > value_type value_or( U && v ) && { return has_value() ? std::move( contained.value() ) : static_cast( std::forward( v ) ); } // unwrap() // template // constexpr expected expected,E>::unwrap() const&; // template // constexpr expected expected::unwrap() const&; // template // expected expected, E>::unwrap() &&; // template // template expected expected::unwrap() &&; // factories // template< typename Ex, typename F> // expected catch_exception(F&& f); // template< typename F> // expected())),E> map(F&& func) ; // template< typename F> // 'see below' bind(F&& func); // template< typename F> // expected catch_error(F&& f); // template< typename F> // 'see below' then(F&& func); private: detail::storage_t < T ,E , std::is_copy_constructible::value && std::is_copy_constructible::value , std::is_move_constructible::value && std::is_move_constructible::value > contained; }; /// class expected, void specialization template< typename E > class expected { private: template< typename, typename > friend class expected; public: using value_type = void; using error_type = E; using unexpected_type = nonstd::unexpected_type; // x.x.4.1 constructors constexpr expected() noexcept : contained( true ) {} nsel_constexpr14 expected( expected const & other ) = default; nsel_constexpr14 expected( expected && other ) = default; constexpr explicit expected( nonstd_lite_in_place_t(void) ) : contained( true ) {} template< typename G = E nsel_REQUIRES_T( !std::is_convertible::value /*=> explicit */ ) > nsel_constexpr14 explicit expected( nonstd::unexpected_type const & error ) : contained( false ) { contained.construct_error( E{ error.value() } ); } template< typename G = E nsel_REQUIRES_T( std::is_convertible::value /*=> non-explicit */ ) > nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type const & error ) : contained( false ) { contained.construct_error( error.value() ); } template< typename G = E nsel_REQUIRES_T( !std::is_convertible::value /*=> explicit */ ) > nsel_constexpr14 explicit expected( nonstd::unexpected_type && error ) : contained( false ) { contained.construct_error( E{ std::move( error.value() ) } ); } template< typename G = E nsel_REQUIRES_T( std::is_convertible::value /*=> non-explicit */ ) > nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type && error ) : contained( false ) { contained.construct_error( std::move( error.value() ) ); } template< typename... Args nsel_REQUIRES_T( std::is_constructible::value ) > nsel_constexpr14 explicit expected( unexpect_t, Args&&... args ) : contained( false ) { contained.emplace_error( std::forward( args )... ); } template< typename U, typename... Args nsel_REQUIRES_T( std::is_constructible, Args&&...>::value ) > nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list il, Args&&... args ) : contained( false ) { contained.emplace_error( il, std::forward( args )... ); } // destructor ~expected() { if ( ! has_value() ) { contained.destruct_error(); } } // x.x.4.3 assignment expected & operator=( expected const & other ) { expected( other ).swap( *this ); return *this; } expected & operator=( expected && other ) noexcept ( std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value ) { expected( std::move( other ) ).swap( *this ); return *this; } void emplace() { expected().swap( *this ); } // x.x.4.4 swap template< typename G = E > nsel_REQUIRES_R( void, std17::is_swappable::value && std::is_move_constructible::value ) swap( expected & other ) noexcept ( std::is_nothrow_move_constructible::value && std17::is_nothrow_swappable::value ) { using std::swap; if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); } else if ( bool(*this) && ! bool(other) ) { contained.construct_error( std::move( other.error() ) ); bool has_value = contained.has_value(); bool other_has_value = other.has_value(); other.contained.set_has_value(has_value); contained.set_has_value(other_has_value); } else if ( ! bool(*this) && bool(other) ) { other.swap( *this ); } } // x.x.4.5 observers constexpr explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return contained.has_value(); } void value() const { if ( ! has_value() ) { error_traits::rethrow( contained.error() ); } } constexpr error_type const & error() const & { return assert( ! has_value() ), contained.error(); } error_type & error() & { return assert( ! has_value() ), contained.error(); } #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 constexpr error_type const && error() const && { return std::move( ( assert( ! has_value() ), contained.error() ) ); } error_type && error() && { return std::move( ( assert( ! has_value() ), contained.error() ) ); } #endif constexpr unexpected_type get_unexpected() const { return make_unexpected( contained.error() ); } template< typename Ex > bool has_exception() const { using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type; return ! has_value() && std::is_base_of< Ex, ContainedEx>::value; } // template constexpr 'see below' unwrap() const&; // // template 'see below' unwrap() &&; // factories // template< typename Ex, typename F> // expected catch_exception(F&& f); // // template< typename F> // expected map(F&& func) ; // // template< typename F> // 'see below' bind(F&& func) ; // // template< typename F> // expected catch_error(F&& f); // // template< typename F> // 'see below' then(F&& func); private: detail::storage_t < void , E , std::is_copy_constructible::value , std::is_move_constructible::value > contained; }; // x.x.4.6 expected<>: comparison operators template< typename T1, typename E1, typename T2, typename E2 nsel_REQUIRES_T( !std::is_void::value && !std::is_void::value ) > constexpr bool operator==( expected const & x, expected const & y ) { return bool(x) != bool(y) ? false : bool(x) ? *x == *y : x.error() == y.error(); } template< typename T1, typename E1, typename T2, typename E2 nsel_REQUIRES_T( std::is_void::value && std::is_void::value ) > constexpr bool operator==( expected const & x, expected const & y ) { return bool(x) != bool(y) ? false : bool(x) || static_cast( x.error() == y.error() ); } template< typename T1, typename E1, typename T2, typename E2 > constexpr bool operator!=( expected const & x, expected const & y ) { return !(x == y); } #if nsel_P0323R <= 2 template< typename T, typename E > constexpr bool operator<( expected const & x, expected const & y ) { return (!y) ? false : (!x) ? true : *x < *y; } template< typename T, typename E > constexpr bool operator>( expected const & x, expected const & y ) { return (y < x); } template< typename T, typename E > constexpr bool operator<=( expected const & x, expected const & y ) { return !(y < x); } template< typename T, typename E > constexpr bool operator>=( expected const & x, expected const & y ) { return !(x < y); } #endif // x.x.4.7 expected: comparison with T template< typename T1, typename E1, typename T2 nsel_REQUIRES_T( !std::is_void::value ) > constexpr bool operator==( expected const & x, T2 const & v ) { return bool(x) ? *x == v : false; } template< typename T1, typename E1, typename T2 nsel_REQUIRES_T( !std::is_void::value ) > constexpr bool operator==(T2 const & v, expected const & x ) { return bool(x) ? v == *x : false; } template< typename T1, typename E1, typename T2 > constexpr bool operator!=( expected const & x, T2 const & v ) { return bool(x) ? *x != v : true; } template< typename T1, typename E1, typename T2 > constexpr bool operator!=( T2 const & v, expected const & x ) { return bool(x) ? v != *x : true; } #if nsel_P0323R <= 2 template< typename T, typename E > constexpr bool operator<( expected const & x, T const & v ) { return bool(x) ? *x < v : true; } template< typename T, typename E > constexpr bool operator<( T const & v, expected const & x ) { return bool(x) ? v < *x : false; } template< typename T, typename E > constexpr bool operator>( T const & v, expected const & x ) { return bool(x) ? *x < v : false; } template< typename T, typename E > constexpr bool operator>( expected const & x, T const & v ) { return bool(x) ? v < *x : false; } template< typename T, typename E > constexpr bool operator<=( T const & v, expected const & x ) { return bool(x) ? ! ( *x < v ) : false; } template< typename T, typename E > constexpr bool operator<=( expected const & x, T const & v ) { return bool(x) ? ! ( v < *x ) : true; } template< typename T, typename E > constexpr bool operator>=( expected const & x, T const & v ) { return bool(x) ? ! ( *x < v ) : false; } template< typename T, typename E > constexpr bool operator>=( T const & v, expected const & x ) { return bool(x) ? ! ( v < *x ) : true; } #endif // nsel_P0323R // x.x.4.8 expected: comparison with unexpected_type template< typename T1, typename E1 , typename E2 > constexpr bool operator==( expected const & x, unexpected_type const & u ) { return (!x) ? x.get_unexpected() == u : false; } template< typename T1, typename E1 , typename E2 > constexpr bool operator==( unexpected_type const & u, expected const & x ) { return ( x == u ); } template< typename T1, typename E1 , typename E2 > constexpr bool operator!=( expected const & x, unexpected_type const & u ) { return ! ( x == u ); } template< typename T1, typename E1 , typename E2 > constexpr bool operator!=( unexpected_type const & u, expected const & x ) { return ! ( x == u ); } #if nsel_P0323R <= 2 template< typename T, typename E > constexpr bool operator<( expected const & x, unexpected_type const & u ) { return (!x) ? ( x.get_unexpected() < u ) : false; } template< typename T, typename E > constexpr bool operator<( unexpected_type const & u, expected const & x ) { return (!x) ? ( u < x.get_unexpected() ) : true ; } template< typename T, typename E > constexpr bool operator>( expected const & x, unexpected_type const & u ) { return ( u < x ); } template< typename T, typename E > constexpr bool operator>( unexpected_type const & u, expected const & x ) { return ( x < u ); } template< typename T, typename E > constexpr bool operator<=( expected const & x, unexpected_type const & u ) { return ! ( u < x ); } template< typename T, typename E > constexpr bool operator<=( unexpected_type const & u, expected const & x) { return ! ( x < u ); } template< typename T, typename E > constexpr bool operator>=( expected const & x, unexpected_type const & u ) { return ! ( u > x ); } template< typename T, typename E > constexpr bool operator>=( unexpected_type const & u, expected const & x ) { return ! ( x > u ); } #endif // nsel_P0323R /// x.x.x Specialized algorithms template< typename T, typename E nsel_REQUIRES_T( ( std::is_void::value || std::is_move_constructible::value ) && std::is_move_constructible::value && std17::is_swappable::value && std17::is_swappable::value ) > void swap( expected & x, expected & y ) noexcept ( noexcept ( x.swap(y) ) ) { x.swap( y ); } #if nsel_P0323R <= 3 template< typename T > constexpr auto make_expected( T && v ) -> expected< typename std::decay::type > { return expected< typename std::decay::type >( std::forward( v ) ); } // expected specialization: auto inline make_expected() -> expected { return expected( in_place ); } template< typename T > constexpr auto make_expected_from_current_exception() -> expected { return expected( make_unexpected_from_current_exception() ); } template< typename T > auto make_expected_from_exception( std::exception_ptr v ) -> expected { return expected( unexpected_type( std::forward( v ) ) ); } template< typename T, typename E > constexpr auto make_expected_from_error( E e ) -> expected::type> { return expected::type>( make_unexpected( e ) ); } template< typename F nsel_REQUIRES_T( ! std::is_same::type, void>::value ) > /*nsel_constexpr14*/ auto make_expected_from_call( F f ) -> expected< typename std::result_of::type > { try { return make_expected( f() ); } catch (...) { return make_unexpected_from_current_exception(); } } template< typename F nsel_REQUIRES_T( std::is_same::type, void>::value ) > /*nsel_constexpr14*/ auto make_expected_from_call( F f ) -> expected { try { f(); return make_expected(); } catch (...) { return make_unexpected_from_current_exception(); } } #endif // nsel_P0323R } // namespace expected_lite using namespace expected_lite; // using expected_lite::expected; // using ... } // namespace nonstd namespace std { // expected: hash support template< typename T, typename E > struct hash< nonstd::expected > { using result_type = std::size_t; using argument_type = nonstd::expected; constexpr result_type operator()(argument_type const & arg) const { return arg ? std::hash{}(*arg) : result_type{}; } }; // TBD - ?? remove? see spec. template< typename T, typename E > struct hash< nonstd::expected > { using result_type = std::size_t; using argument_type = nonstd::expected; constexpr result_type operator()(argument_type const & arg) const { return arg ? std::hash{}(*arg) : result_type{}; } }; // TBD - implement // bool(e), hash>()(e) shall evaluate to the hashing true; // otherwise it evaluates to an unspecified value if E is exception_ptr or // a combination of hashing false and hash()(e.error()). template< typename E > struct hash< nonstd::expected > { }; } // namespace std namespace nonstd { // void unexpected() is deprecated && removed in C++17 #if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141 template< typename E > using unexpected = unexpected_type; #endif } // namespace nonstd #undef nsel_REQUIRES #undef nsel_REQUIRES_0 #undef nsel_REQUIRES_T nsel_RESTORE_WARNINGS() #endif // nsel_USES_STD_EXPECTED #endif // NONSTD_EXPECTED_LITE_HPP