// Copyright (C) 2016 Martin Moene. // // This version targets C++11 and later. // // This code is licensed under the MIT License (MIT). // // expected lite is based on: // A proposal to add a utility class to represent expected monad - Revision 2 // by Vicente J. Botet Escriba and Pierre Talbot. #ifndef OPENTRACING_EXPECTED_LITE_HPP #define OPENTRACING_EXPECTED_LITE_HPP #include #include #include #include #include #include #include #include #include #define expected_lite_VERSION "0.0.0" // Compiler detection: #define nsel_CPP11_OR_GREATER ( __cplusplus >= 201103L ) #define nsel_CPP14_OR_GREATER ( __cplusplus >= 201402L ) #if nsel_CPP14_OR_GREATER # define nsel_constexpr14 constexpr #else # define nsel_constexpr14 /*constexpr*/ #endif // Method enabling #define nsel_REQUIRES(...) \ typename std::enable_if<__VA_ARGS__, void*>::type = 0 #define nsel_REQUIRES_0(...) \ template< bool B = (__VA_ARGS__), typename std::enable_if::type = 0 > #define nsel_REQUIRES_T(...) \ typename = typename std::enable_if< (__VA_ARGS__), opentracing::expected_detail::enabler >::type namespace opentracing { BEGIN_OPENTRACING_ABI_NAMESPACE template< typename T, typename E > class expected; namespace expected_detail { /// for nsel_REQUIRES_T enum class enabler{}; /// discriminated union to hold value or 'error'. template< typename T, typename E > union storage_t { friend class expected; private: typedef T value_type; typedef E error_type; // no-op construction storage_t() {} ~storage_t() {} void construct_value( value_type const & v ) { new( &m_value ) value_type( v ); } void construct_value( value_type && v ) { new( &m_value ) value_type( std::forward( v ) ); } 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 ) ); } 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 && value() const && { return std::move( m_value ); } const value_type * 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; } private: value_type m_value; error_type m_error; }; /// discriminated union to hold only 'error'. template< typename E > union storage_t { friend class expected; private: typedef void value_type; typedef E error_type; // no-op construction storage_t() {} ~storage_t() {} 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 ) ); } void destruct_error() { m_error.~error_type(); } error_type const & error() const { return m_error; } error_type & error() { return m_error; } private: error_type m_error; }; } // namespace expected_detail /// class unexpected_type template< typename E = std::error_code > class unexpected_type { public: typedef E error_type; unexpected_type() = delete; nsel_REQUIRES_0( std::is_copy_constructible::value ) constexpr explicit unexpected_type( error_type const & error ) : m_error( error ) {} nsel_REQUIRES_0( std::is_move_constructible::value ) constexpr explicit unexpected_type( error_type && error ) : m_error( std::move( error ) ) {} constexpr error_type const & value() const { return m_error; } error_type & value() { return m_error; } private: error_type m_error; }; /// class unexpected_type, std::exception_ptr specialization template<> class unexpected_type< std::exception_ptr > { public: typedef std::exception_ptr error_type; 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; }; // unexpected: relational operators 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 ! ( x == y ); } 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 ); } 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 ); } // unexpected: traits template struct is_unexpected : std::false_type {}; template struct is_unexpected< unexpected_type > : std::true_type {}; // unexpected: factory template< typename E> nsel_constexpr14 auto make_unexpected( E && v) -> unexpected_type< typename std::decay::type > { return unexpected_type< typename std::decay::type >( v ); } /*nsel_constexpr14*/ inline auto make_unexpected_from_current_exception() -> unexpected_type< std::exception_ptr > { return unexpected_type< std::exception_ptr >( std::current_exception() ); } /// in-place tag: construct a value in-place (should come from std::experimental::optional) struct in_place_t{}; constexpr in_place_t in_place{}; /// unexpect tag, in_place_unexpected tag: construct an error struct in_place_unexpected_t{}; constexpr in_place_unexpected_t unexpect{}; constexpr in_place_unexpected_t in_place_unexpected{}; /// expected access error template< typename E > class bad_expected_access : public std::logic_error { public: typedef E error_type; explicit bad_expected_access( error_type error ) : logic_error( "bad_expected_access" ) , m_error( error ) {} constexpr error_type const & error() const { return m_error; } error_type & error() { return m_error; } private: error_type m_error; }; /// class error_traits 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 ); } }; /// class expected template< typename T, typename E = std::error_code > class expected { public: typedef T value_type; typedef E error_type; // constructors nsel_REQUIRES_0( std::is_default_constructible::value ) nsel_constexpr14 expected() noexcept ( std::is_nothrow_default_constructible::value ) : has_value_( true ) { contained.construct_value( value_type() ); } // nsel_REQUIRES_0( // std::is_copy_constructible::value && // std::is_copy_constructible::value ) nsel_constexpr14 expected( expected const & rhs ) : has_value_( rhs.has_value_ ) { if ( has_value() ) contained.construct_value( rhs.contained.value() ); else contained.construct_error( rhs.contained.error() ); } // nsel_REQUIRES_0( // std::is_move_constructible::value && // std::is_move_constructible::value ) nsel_constexpr14 expected( expected && rhs ) : has_value_( rhs.has_value_ ) { if ( has_value() ) contained.construct_value( std::move( rhs.contained.value() ) ); else contained.construct_error( std::move( rhs.contained.error() ) ); } nsel_REQUIRES_0( std::is_copy_constructible::value ) nsel_constexpr14 expected( value_type const & rhs ) : has_value_( true ) { contained.construct_value( rhs ); } nsel_REQUIRES_0( std::is_move_constructible::value ) nsel_constexpr14 expected( value_type && rhs ) noexcept ( std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value ) : has_value_( true ) { contained.construct_value( std::move( rhs ) ); } template ::value ) > nsel_constexpr14 explicit expected( in_place_t, Args&&... args ) : has_value_( true ) { contained.construct_value( std::forward( args )... ); } template< typename U, typename... Args, nsel_REQUIRES_T( std::is_constructible, Args&&...>::value ) > nsel_constexpr14 explicit expected( in_place_t, std::initializer_list il, Args&&... args ) : has_value_( true ) { contained.construct_value( il, std::forward( args )... ); } nsel_REQUIRES_0( std::is_copy_constructible::value ) nsel_constexpr14 expected( unexpected_type const & error ) : has_value_( false ) { contained.construct_error( error.value() ); } nsel_REQUIRES_0( std::is_move_constructible::value ) nsel_constexpr14 expected( unexpected_type && error ) : has_value_( false ) { contained.construct_error( std::move( error.value() ) ); } template< typename... Args, nsel_REQUIRES_T( std::is_constructible::value ) > nsel_constexpr14 explicit expected( in_place_unexpected_t, Args&&... args ) : has_value_( false ) { contained.construct_error( std::forward( args )... ); } template< typename U, typename... Args, nsel_REQUIRES_T( std::is_constructible, Args&&...>::value ) > nsel_constexpr14 explicit expected( in_place_unexpected_t, std::initializer_list il, Args&&... args ) : has_value_( false ) { contained.construct_error( il, std::forward( args )... ); } // destructor ~expected() { if ( has_value() ) contained.destruct_value(); else contained.destruct_error(); } // assignment // nsel_REQUIRES( // std::is_copy_constructible::value && // std::is_copy_assignable::value && // std::is_copy_constructible::value && // std::is_copy_assignable::value ) expected operator=( expected const & rhs ) { expected( rhs ).swap( *this ); return *this; } // nsel_REQUIRES( // std::is_move_constructible::value && // std::is_move_assignable::value && // std::is_move_constructible::value && // std::is_move_assignable::value ) expected & operator=( expected && rhs ) noexcept ( std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value ) { expected( std::move( rhs ) ).swap( *this ); return *this; } template< typename U, nsel_REQUIRES_T( std::is_constructible::value && std::is_assignable::value ) > expected & operator=( U && v ) { expected( std::forward( v ) ).swap( *this ); return *this; } // nsel_REQUIRES( // std::is_copy_constructible::value && // std::is_assignable::value ) expected & operator=( unexpected_type const & u ) { expected( std::move( u ) ).swap( *this ); return *this; } // nsel_REQUIRES( // std::is_copy_constructible::value && // std::is_assignable::value ) expected & operator=( unexpected_type && u ) { expected( std::move( u ) ).swap( *this ); return *this; } template< typename... Args, nsel_REQUIRES_T( std::is_constructible::value ) > void emplace( Args &&... args ) { expected( in_place, std::forward(args)... ).swap( *this ); } template< typename U, typename... Args, nsel_REQUIRES_T( std::is_constructible&, Args&&...>::value ) > void emplace( std::initializer_list il, Args &&... args ) { expected( in_place, il, std::forward(args)... ).swap( *this ); } // nsel_REQUIRES( // std::is_move_constructible::value && // std::is_move_constructible::value ) void swap( expected & rhs ) noexcept ( std::is_nothrow_move_constructible::value && noexcept( std::swap( std::declval(), std::declval() ) ) && std::is_nothrow_move_constructible::value && noexcept( std::swap( std::declval(), std::declval() ) ) ) { using std::swap; if ( bool(*this) && bool(rhs) ) { swap( contained.value(), rhs.contained.value() ); } else if ( ! bool(*this) && ! bool(rhs) ) { swap( contained.error(), rhs.contained.error() ); } else if ( bool(*this) && ! bool(rhs) ) { error_type t( std::move( rhs.error() ) ); // TBD - ?? rhs.contained.destruct_error(); rhs.contained.construct_value( std::move( contained.value() ) ); // TBD - ?? contained.destruct_value(); contained.construct_error( std::move( t ) ); swap( has_value_, rhs.has_value_ ); } else if ( ! bool(*this) && bool(rhs) ) { rhs.swap( *this ); } } // 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(); } constexpr value_type && operator *() const && { return assert( has_value() ), std::move( contained.value() ); } value_type && operator *() && { return assert( has_value() ), std::move( contained.value() ); } constexpr explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return 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() ); } value_type && value() && { return has_value() ? ( contained.value() ) : ( error_traits::rethrow( contained.error() ), contained.value() ); } constexpr error_type const & error() const & { return assert( ! has_value() ), contained.error(); } error_type & error() & { return assert( ! has_value() ), contained.error(); } constexpr error_type && error() const && { return assert( ! has_value() ), std::move( contained.error() ); } constexpr unexpected_type get_unexpected() const { return make_unexpected( contained.error() ); } template< typename Ex > bool has_exception() const { return ! has_value() && std::is_base_of< Ex, decltype( get_unexpected().value() ) >::value; } template< typename U, nsel_REQUIRES_T( std::is_copy_constructible::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::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 // expected catch_exception(F&& f); // template // expected())),E> map(F&& func) ; // template // ’see below’ bind(F&& func); // template // expected catch_error(F&& f); // template // ’see below’ then(F&& func); private: bool has_value_; expected_detail::storage_t contained; }; /// class expected, void specialization template< typename E > class expected { public: typedef void value_type; typedef E error_type; template< typename U > struct rebind { typedef expected type; }; // constructors constexpr expected() noexcept : has_value_( true ) { } // nsel_REQUIRES_0( // std::is_copy_constructible::value ) nsel_constexpr14 expected( expected const & rhs ) : has_value_( rhs.has_value_ ) { if ( ! has_value() ) contained.construct_error( rhs.contained.error() ); } nsel_REQUIRES_0( std::is_move_constructible::value ) nsel_constexpr14 expected( expected && rhs ) noexcept ( true // TBD - see also non-void specialization ) : has_value_( rhs.has_value_ ) { if ( ! has_value() ) contained.construct_error( std::move( rhs.contained.error() ) ); } //nsel_REQUIRES_0( // std::is_default_constructible::value ) constexpr explicit expected( in_place_t ) : has_value_( true ) { } // nsel_REQUIRES( // std::is_copy_constructible::value && // std::is_assignable::value ) nsel_constexpr14 expected( unexpected_type const & error ) : has_value_( false ) { contained.construct_error( error.value() ); } // ?? expected( unexpected_type && error ) template nsel_constexpr14 expected( unexpected_type const & error ) : has_value_( false ) { contained.construct_error( error.value() ); } // destructor ~expected() { if ( ! has_value() ) contained.destruct_error(); } // assignment // nsel_REQUIRES( // std::is_copy_constructible::value && // std::is_copy_assignable::value ) expected & operator=(expected const & rhs ) { expected( rhs ).swap( *this ); return *this; } // nsel_REQUIRES( // std::is_move_constructible::value && // std::is_move_assignable::value ) expected & operator=( expected && rhs ) noexcept ( std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value ) { expected( std::move( rhs ) ).swap( *this ); return *this; } void emplace() {} // swap // nsel_REQUIRES( // std::is_move_constructible::value ) void swap( expected & rhs ) noexcept ( std::is_nothrow_move_constructible::value && noexcept( std::swap( std::declval(), std::declval() ) ) ) { using std::swap; if ( ! bool(*this) && ! bool(rhs) ) { swap( contained.error(), rhs.contained.error() ); } else if ( bool(*this) && ! bool(rhs) ) { contained.construct_error( std::move( rhs.error() ) ); swap( has_value_, rhs.has_value_ ); } else if ( ! bool(*this) && bool(rhs) ) { rhs.swap( *this ); } } // observers constexpr explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return has_value_; } void value() const {} constexpr error_type const & error() const & { return assert( ! has_value() ), contained.error(); } error_type & error() & { return assert( ! has_value() ), contained.error(); } constexpr error_type && error() const && { return assert( ! has_value() ), std::move( contained.error() ); } constexpr unexpected_type get_unexpected() const { return make_unexpected( contained.error() ); } template bool has_exception() const { return ! has_value() && std::is_base_of< Ex, decltype( get_unexpected().value() ) >::value; } // template constexpr ’see below’ unwrap() const&; // // template ’see below’ unwrap() &&; // factories // template // expected catch_exception(F&& f); // // template // expected map(F&& func) ; // // template // ’see below’ bind(F&& func) ; // // template // expected catch_error(F&& f); // // template // ’see below’ then(F&& func); private: bool has_value_; expected_detail::storage_t contained; }; // expected: relational operators template constexpr bool operator==( expected const & x, expected const & y ) { return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; } template constexpr bool operator!=( expected const & x, expected const & y ) { return !(x == y); } template constexpr bool operator<( expected const & x, expected const & y ) { return (!y) ? false : (!x) ? true : *x < *y; } template constexpr bool operator>( expected const & x, expected const & y ) { return (y < x); } template constexpr bool operator<=( expected const & x, expected const & y ) { return !(y < x); } template constexpr bool operator>=( expected const & x, expected const & y ) { return !(x < y); } // expected: comparison with unexpected_type template constexpr bool operator==( expected const & x, unexpected_type const & u ) { return (!x) ? x.get_unexpected() == u : false; } template constexpr bool operator==( unexpected_type const & u, expected const & x ) { return ( x == u ); } template constexpr bool operator!=( expected const & x, unexpected_type const & u ) { return ! ( x == u ); } template constexpr bool operator!=( unexpected_type const & u, expected const & x ) { return ! ( x == u ); } template constexpr bool operator<( expected const & x, unexpected_type const & u ) { return (!x) ? ( x.get_unexpected() < u ) : false; } template constexpr bool operator<( unexpected_type const & u, expected const & x ) { return (!x) ? ( u < x.get_unexpected() ) : true ; } template constexpr bool operator>( expected const & x, unexpected_type const & u ) { return ( u < x ); } template constexpr bool operator>( unexpected_type const & u, expected const & x ) { return ( x < u ); } template constexpr bool operator<=( expected const & x, unexpected_type const & u ) { return ! ( u < x ); } template constexpr bool operator<=( unexpected_type const & u, expected const & x) { return ! ( x < u ); } template constexpr bool operator>=( expected const & x, unexpected_type const & u ) { return ! ( u > x ); } template constexpr bool operator>=( unexpected_type const & u, expected const & x ) { return ! ( x > u ); } // expected: comparison with T template constexpr bool operator==( expected const & x, T const & v ) { return bool(x) ? *x == v : false; } template constexpr bool operator==(T const & v, expected const & x ) { return bool(x) ? v == *x : false; } template constexpr bool operator!=( expected const & x, T const & v ) { return bool(x) ? *x != v : true; } template constexpr bool operator!=( T const & v, expected const & x ) { return bool(x) ? v != *x : true; } template constexpr bool operator<( expected const & x, T const & v ) { return bool(x) ? *x < v : true; } template constexpr bool operator<( T const & v, expected const & x ) { return bool(x) ? v < *x : false; } template constexpr bool operator>( T const & v, expected const & x ) { return bool(x) ? *x < v : false; } template constexpr bool operator>( expected const & x, T const & v ) { return bool(x) ? v < *x : false; } template constexpr bool operator<=( T const & v, expected const & x ) { return bool(x) ? ! ( *x < v ) : false; } template constexpr bool operator<=( expected const & x, T const & v ) { return bool(x) ? ! ( v < *x ) : true; } template constexpr bool operator>=( expected const & x, T const & v ) { return bool(x) ? ! ( *x < v ) : false; } template constexpr bool operator>=( T const & v, expected const & x ) { return bool(x) ? ! ( v < *x ) : true; } // expected: specialized algorithms template< typename T, typename E > void swap( expected & x, expected & y ) noexcept( noexcept( x.swap(y) ) ) { x.swap( y ); } 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: inline auto make_expected() -> expected { return expected( in_place ); } template< typename T, typename E > constexpr auto make_expected_from_error( E e ) -> expected::type> { return expected::type>( make_unexpected( e ) ); } END_OPENTRACING_ABI_NAMESPACE } // namespace opentracing namespace std { // expected: hash support template< typename T, typename E > struct hash< opentracing::expected > { typedef typename hash::result_type result_type; typedef opentracing::expected argument_type; 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< opentracing::expected > { typedef typename hash::result_type result_type; typedef opentracing::expected argument_type; 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< opentracing::expected > { }; } // namespace std #undef nsel_REQUIRES #undef nsel_REQUIRES_0 #undef nsel_REQUIRES_T #endif // OPENTRACING_EXPECTED_LITE_HPP