diff --git a/include/nonstd/expected.hpp b/include/nonstd/expected.hpp index c18d1db..91681b1 100644 --- a/include/nonstd/expected.hpp +++ b/include/nonstd/expected.hpp @@ -452,6 +452,12 @@ class expected; namespace detail { +template< typename T > +struct is_expected : std::false_type {}; + +template< typename T, typename E > +struct is_expected< expected< T, E > > : std::true_type {}; + /// discriminated union to hold value or 'error'. template< typename T, typename E > @@ -990,6 +996,130 @@ class storage_t : public storage_t_impl } }; +// C++11 invoke implementation +template< typename > +struct is_reference_wrapper : std::false_type {}; +template< typename T > +struct is_reference_wrapper< std::reference_wrapper< T > > : std::true_type {}; + +template< typename FnT, typename ClassT, typename ObjectT, typename... Args + nsel_REQUIRES_T( + std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_function_impl( FnT ( ClassT::* memfnptr), ObjectT && obj, Args && ... args ) + noexcept( noexcept( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... ) ) ) + -> decltype( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )...) ) +{ + return (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... ); +} + +template< typename FnT, typename ClassT, typename ObjectT, typename... Args + nsel_REQUIRES_T( + is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_function_impl( FnT ( ClassT::* memfnptr ), ObjectT && obj, Args && ... args ) + noexcept( noexcept( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) ) ) + -> decltype( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) ) +{ + return (obj.get().*memfnptr)( std::forward< Args >( args ) ... ); +} + +template< typename FnT, typename ClassT, typename ObjectT, typename... Args + nsel_REQUIRES_T( + !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_function_impl( FnT ( ClassT::* memfnptr ), ObjectT && obj, Args && ... args ) + noexcept( noexcept( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) ) ) + -> decltype( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) ) +{ + return ((*std::forward(obj)).*memfnptr)( std::forward< Args >( args ) ... ); +} + +template< typename MemberT, typename ClassT, typename ObjectT + nsel_REQUIRES_T( + std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj ) + noexcept( noexcept( std::forward< ObjectT >( obj ).*memobjptr ) ) + -> decltype( std::forward< ObjectT >( obj ).*memobjptr ) +{ + return std::forward< ObjectT >( obj ).*memobjptr; +} + +template< typename MemberT, typename ClassT, typename ObjectT + nsel_REQUIRES_T( + is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj ) + noexcept( noexcept( obj.get().*memobjptr ) ) + -> decltype( obj.get().*memobjptr ) +{ + return obj.get().*memobjptr; +} + +template< typename MemberT, typename ClassT, typename ObjectT + nsel_REQUIRES_T( + !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value + && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value + ) +> +nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj ) + noexcept( noexcept( (*std::forward< ObjectT >( obj )).*memobjptr ) ) + -> decltype( (*std::forward< ObjectT >( obj )).*memobjptr ) +{ + return (*std::forward< ObjectT >( obj )).*memobjptr; +} + +template< typename F, typename... Args + nsel_REQUIRES_T( + std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value + ) +> +nsel_constexpr auto invoke( F && f, Args && ... args ) + noexcept( noexcept( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) ) + -> decltype( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) +{ + return invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ); +} + +template< typename F, typename... Args + nsel_REQUIRES_T( + std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value + ) +> +nsel_constexpr auto invoke( F && f, Args && ... args ) + noexcept( noexcept( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) ) + -> decltype( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) +{ + return invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ); +} + +template< typename F, typename... Args + nsel_REQUIRES_T( + !std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value + && !std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value + ) +> +nsel_constexpr auto invoke( F && f, Args && ... args ) + noexcept( noexcept( std::forward< F >( f )( std::forward< Args >( args ) ... ) ) ) + -> decltype( std::forward< F >( f )( std::forward< Args >( args ) ... ) ) +{ + return std::forward< F >( f )( std::forward< Args >( args ) ... ); +} + +template< typename F, typename ... Args > +using invoke_result_nocvref_t = typename std20::remove_cvref< decltype( invoke( std::declval< F >(), std::declval< Args >()... ) ) >::type; + } // namespace detail /// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also use aliased type unexpected. @@ -1498,6 +1628,22 @@ struct error_traits< std::error_code > #endif // nsel_CONFIG_NO_EXCEPTIONS +namespace detail { + +// from https://en.cppreference.com/w/cpp/utility/expected/unexpected: +// "the type of the unexpected value. The type must not be an array type, a non-object type, a specialization of std::unexpected, or a cv-qualified type." +template< typename T > +struct valid_unexpected_type : std::integral_constant< bool, + std::is_same< T, typename std20::remove_cvref< T >::type >::value + && std::is_object< T >::value + && !std::is_array< T >::value +> {}; + +template< typename T > +struct valid_unexpected_type< unexpected_type< T > > : std::false_type {}; + +} // namespace detail + } // namespace expected_lite // provide nonstd::unexpected_type: @@ -2043,6 +2189,282 @@ class expected : static_cast( std::forward( v ) ); } + // Monadic operations (P2505) + template< typename F + nsel_REQUIRES_T( + detail::is_expected < detail::invoke_result_nocvref_t< F, value_type & > > ::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type & >::error_type, error_type >::value + && std::is_constructible< error_type, error_type & >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type & > and_then( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, value_type & >( detail::invoke( std::forward< F >( f ), value() ) ) + : detail::invoke_result_nocvref_t< F, value_type & >( unexpect, error() ); + } + + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type & >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type & > and_then( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const value_type & >( detail::invoke( std::forward< F >( f ), value() ) ) + : detail::invoke_result_nocvref_t< F, const value_type & >( unexpect, error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type && >::error_type, error_type >::value + && std::is_constructible< error_type, error_type && >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type && > and_then( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) ) + : detail::invoke_result_nocvref_t< F, value_type && >( unexpect, std::move( error() ) ); + } + + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type && >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type && > and_then( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) ) + : detail::invoke_result_nocvref_t< F, const value_type && >( unexpect, std::move( error() ) ); + } +#endif + + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type, value_type >::value + && std::is_constructible< value_type, value_type & >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type & >( value() ) + : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type, value_type >::value + && std::is_constructible< value_type, const value_type & >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type & >( value() ) + : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type, value_type >::value + && std::is_constructible< value_type, value_type && >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type && >( std::move( value() ) ) + : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type, value_type >::value + && std::is_constructible< value_type, const value_type && >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type && >( std::move( value() ) ) + : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif + + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F, value_type & > >::value + ) + > + nsel_constexpr14 expected< detail::invoke_result_nocvref_t< F, value_type & >, error_type > transform( F && f ) & + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F, value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) ) + : make_unexpected( error() ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F, value_type & > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F, const value_type & > >::value + ) + > + nsel_constexpr expected< detail::invoke_result_nocvref_t< F, const value_type & >, error_type > transform( F && f ) const & + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F, const value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) ) + : make_unexpected( error() ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F, const value_type & > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F, value_type && > >::value + ) + > + nsel_constexpr14 expected< detail::invoke_result_nocvref_t< F, value_type && >, error_type > transform( F && f ) && + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F, value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) ) + : make_unexpected( std::move( error() ) ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F, value_type && > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( std::move( error() ) ); + } + + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F, const value_type && > >::value + ) + > + nsel_constexpr expected< detail::invoke_result_nocvref_t< F, const value_type && >, error_type > transform( F && f ) const && + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F, const value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) ) + : make_unexpected( std::move( error() ) ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F, const value_type && > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() ) + : make_unexpected( std::move( error() ) ); + } +#endif + + template >::value + && std::is_constructible< value_type, value_type & >::value + ) + > + nsel_constexpr14 expected< value_type, detail::invoke_result_nocvref_t< F, error_type & > > transform_error( F && f ) & + { + return has_value() + ? expected< value_type, detail::invoke_result_nocvref_t< F, error_type & > >( in_place, **this ) + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template >::value + && std::is_constructible< value_type, const value_type & >::value + ) + > + nsel_constexpr expected< value_type, detail::invoke_result_nocvref_t< F, const error_type & > > transform_error( F && f ) const & + { + return has_value() + ? expected< value_type, detail::invoke_result_nocvref_t< F, const error_type & > >( in_place, **this ) + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template >::value + && std::is_constructible< value_type, value_type && >::value + ) + > + nsel_constexpr14 expected< value_type, detail::invoke_result_nocvref_t< F, error_type && > > transform_error( F && f ) && + { + return has_value() + ? expected< value_type, detail::invoke_result_nocvref_t< F, error_type && > >( in_place, std::move( **this ) ) + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template >::value + && std::is_constructible< value_type, const value_type && >::value + ) + > + nsel_constexpr expected< value_type, detail::invoke_result_nocvref_t< F, const error_type && > > transform_error( F && f ) const && + { + return has_value() + ? expected< value_type, detail::invoke_result_nocvref_t< F, const error_type && > >( in_place, std::move( **this ) ) + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif // unwrap() // template @@ -2289,6 +2711,275 @@ class expected return ! has_value() && std::is_base_of< Ex, ContainedEx>::value; } + // Monadic operations (P2505) + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, error_type & >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, error() ); + } + + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type & >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, error_type && >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) ); + } + + template >::value + && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value + && std::is_constructible< error_type, const error_type && >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) ) + : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) ); + } +#endif + + template >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type & >() + : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const & + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type & >() + : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type >::value + ) + > + nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, error_type && >() + : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template >::value + && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type >::value + ) + > + nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const && + { + return has_value() + ? detail::invoke_result_nocvref_t< F, const error_type && >() + : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif + + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr14 expected< detail::invoke_result_nocvref_t< F >, error_type > transform( F && f ) & + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr expected< detail::invoke_result_nocvref_t< F >, error_type > transform( F && f ) const & + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const & + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr14 expected< detail::invoke_result_nocvref_t< F >, error_type > transform( F && f ) && + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr14 expected< void, error_type > transform( F && f ) && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } + + template::value + && !std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr expected< detail::invoke_result_nocvref_t< F >, error_type > transform( F && f ) const && + { + return has_value() + ? expected< detail::invoke_result_nocvref_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) ) + : make_unexpected( error() ); + } + + template::value + && std::is_void< detail::invoke_result_nocvref_t< F > >::value + ) + > + nsel_constexpr expected< void, error_type > transform( F && f ) const && + { + return has_value() + ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() ) + : make_unexpected( error() ); + } +#endif + + template >::value + ) + > + nsel_constexpr14 expected< void, detail::invoke_result_nocvref_t< F, error_type & > > transform_error( F && f ) & + { + return has_value() + ? expected< void, detail::invoke_result_nocvref_t< F, error_type & > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + + template >::value + ) + > + nsel_constexpr expected< void, detail::invoke_result_nocvref_t< F, const error_type & > > transform_error( F && f ) const & + { + return has_value() + ? expected< void, detail::invoke_result_nocvref_t< F, const error_type & > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) ); + } + +#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 + template >::value + ) + > + nsel_constexpr14 expected< void, detail::invoke_result_nocvref_t< F, error_type && > > transform_error( F && f ) && + { + return has_value() + ? expected< void, detail::invoke_result_nocvref_t< F, error_type && > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } + + template >::value + ) + > + nsel_constexpr expected< void, detail::invoke_result_nocvref_t< F, const error_type && > > transform_error( F && f ) const && + { + return has_value() + ? expected< void, detail::invoke_result_nocvref_t< F, const error_type && > >() + : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) ); + } +#endif + // template constexpr 'see below' unwrap() const&; // // template 'see below' unwrap() &&; diff --git a/test/expected.t.cpp b/test/expected.t.cpp index 59ef0dd..12c63f3 100644 --- a/test/expected.t.cpp +++ b/test/expected.t.cpp @@ -1240,6 +1240,107 @@ CASE( "expected: Throws bad_expected_access on value access when disengaged" ) EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access ); } +CASE( "expected: Allows to map value with and_then" ) +{ + const auto mul2 = []( int n ) -> expected { return n * 2; }; + const auto to_unexpect42 = []( int ) -> expected { return make_unexpected( 42 ); }; + + { + expected e{ 11 }; + const expected ce{ 21 }; + expected ue{ unexpect, 42 }; + EXPECT( e.and_then( mul2 ).value() == 22 ); + EXPECT( ce.and_then( mul2 ).value() == 42 ); + EXPECT( !ue.and_then( mul2 ).has_value()); + EXPECT( ue.and_then( mul2 ).error() == 42 ); + EXPECT( !e.and_then( to_unexpect42 ).has_value()); + EXPECT( e.and_then( to_unexpect42 ).error() == 42 ); + EXPECT( ce.and_then( to_unexpect42 ).error() == 42 ); + } + + const auto moveonly_x_mul2 = [](MoveOnly val) -> expected { return val.x * 2; }; + EXPECT( (expected{ MoveOnly{ 33 } }).and_then( moveonly_x_mul2 ).value() == 66 ); + EXPECT( (expected{ MoveOnly{ 15 } }).and_then( [](MoveOnly&&) -> expected { return make_unexpected( 42 ); } ).error() == 42 ); + + const auto map_to_void = [](int) -> expected { return {}; }; + const auto map_to_void_unexpect42 = [](int) -> expected { return make_unexpected( 42 ); }; + static_assert( std::is_same< expected, decltype( expected( 3 ).and_then( map_to_void ) ) >::value, + "and_then mapping to void results in expected"); + EXPECT( (expected(3)).and_then( map_to_void ).has_value() ); + EXPECT( !(expected(3)).and_then( map_to_void_unexpect42 ).has_value() ); + EXPECT( (expected(3)).and_then( map_to_void_unexpect42 ).error() == 42 ); +} + +CASE( "expected: Handling unexpected with or_else" ) +{ + const auto to_unexpect43 = []( int ) -> expected { return make_unexpected( 43 ); }; + + { + expected e{ 11 }; + const expected ce{ 21 }; + expected ue{ unexpect, 42 }; + EXPECT( e.or_else( to_unexpect43 ).has_value()); + EXPECT( e.or_else( to_unexpect43 ).value() == 11 ); + EXPECT( ce.or_else( to_unexpect43 ).value() == 21 ); + EXPECT( !ue.or_else( to_unexpect43 ).has_value()); + EXPECT( ue.or_else( to_unexpect43 ).error() == 43 ); + } + + const auto fallback_throw = []( int ) -> expected { throw std::runtime_error( "or_else" ); }; + EXPECT_THROWS_AS( (expected{ unexpect, 42 }).or_else( fallback_throw ), std::runtime_error ); + + const auto moveonly_fallback_to_66 = [](int) -> expected { return MoveOnly{ 66 }; }; + EXPECT( (expected{ MoveOnly{ 33 } }).or_else( moveonly_fallback_to_66 ).value() == 33 ); + EXPECT( (expected{ unexpect, 15 }).or_else(moveonly_fallback_to_66).value() == 66 ); +} + +CASE( "expected: transform values" ) +{ + const auto mul2 = []( int n ) -> int { return n * 2; }; + + { + expected e{ 11 }; + const expected ce{ 21 }; + expected ue{ unexpect, 42 }; + EXPECT( e.transform( mul2 ).value() == 22 ); + EXPECT( ce.transform( mul2 ).value() == 42 ); + EXPECT( !ue.transform( mul2 ).has_value()); + EXPECT( ue.transform( mul2 ).error() == 42 ); + } + + const auto moveonly_map_to_x = &MoveOnly::x; + const auto moveonly_x_mul2 = [](MoveOnly val) -> int { return val.x * 2; }; + EXPECT( (expected{ MoveOnly{ 33 } }).transform( moveonly_map_to_x ).value() == 33 ); + EXPECT( (expected{ MoveOnly{ 33 } }).transform( moveonly_map_to_x ).transform( mul2 ).value() == 66 ); + EXPECT( (expected{ MoveOnly{ 33 } }).transform( moveonly_x_mul2 ).has_value() ); + EXPECT( (expected{ MoveOnly{ 33 } }).transform( moveonly_x_mul2 ).value() == 66 ); + EXPECT( !(expected{ unexpect, 15 }).transform( [](MoveOnly&&) -> int { return 42; } ).has_value() ); + EXPECT( (expected{ unexpect, 15 }).transform( [](MoveOnly&&) -> int { return 42; } ).error() == 15 ); + + const auto map_to_void = [](int) -> void { }; + static_assert( std::is_same< expected, decltype( expected( 3 ).transform( map_to_void ) ) >::value, + "transform to void results in expected" ); + EXPECT( (expected(3)).transform( map_to_void ).has_value() ); + static_assert( std::is_same< decltype( (expected(3)).transform( map_to_void ).value() ), void >::value, + "transform to void results in void value" ); +} + +CASE( "expected: Mapping errors with transform_error" ) +{ + const auto to_43 = []( int ) -> int { return 43; }; + + { + expected e{ 11 }; + const expected ce{ 21 }; + expected ue{ unexpect, 42 }; + EXPECT( e.transform_error( to_43 ).has_value()); + EXPECT( e.transform_error( to_43 ).value() == 11 ); + EXPECT( ce.transform_error( to_43 ).value() == 21 ); + EXPECT( !ue.transform_error( to_43 ).has_value()); + EXPECT( ue.transform_error( to_43 ).error() == 43 ); + } +} + // ----------------------------------------------------------------------- // expected specialization @@ -1537,6 +1638,99 @@ CASE( "expected: Throws bad_expected_access on value access when disengage EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access ); } +CASE( "expected: calling argless functions with and_then" ) +{ + const auto ret22 = []() -> expected { return 22; }; + const auto unexpect32 = []() -> expected { return make_unexpected( 32 ); }; + + { + expected e; + const expected ce; + expected ue{ unexpect, 42 }; + EXPECT( e.has_value() ); + EXPECT( ce.has_value() ); + EXPECT( e.and_then( ret22 ).value() == 22 ); + EXPECT( ce.and_then( ret22 ).value() == 22 ); + EXPECT( !ue.and_then( ret22 ).has_value()); + EXPECT( ue.and_then( ret22 ).error() == 42 ); + EXPECT( !e.and_then( unexpect32 ).has_value()); + EXPECT( e.and_then( unexpect32 ).error() == 32 ); + EXPECT( ce.and_then( unexpect32 ).error() == 32 ); + } + + { + bool called = false; + expected e; + e.and_then( [&called]() -> expected { + called = true; + return {}; + } ); + EXPECT( called ); + } + + { + bool called = false; + expected{}.and_then( [&called]() -> expected { + called = true; + return {}; + } ); + EXPECT( called ); + } + + { + bool called = false; + expected{ unexpect, 42 }.and_then( [&called]() -> expected { + called = true; + return {}; + } ); + EXPECT( !called ); + } + + const bool map_to_unexpect_success = !expected{}.and_then( []() -> expected { return make_unexpected( 42 ); } ).has_value(); + EXPECT( map_to_unexpect_success ); +} + +CASE( "expected: or_else unexpected handling works" ) +{ + const auto make_valid = [](int) -> expected { return {}; }; + const auto unexpect32 = [](int) -> expected { return make_unexpected( 32 ); }; + + { + expected e; + const expected ce; + expected ue{ unexpect, 42 }; + EXPECT( e.has_value() ); + EXPECT( ce.has_value() ); + EXPECT( e.or_else( unexpect32 ).has_value()); + static_assert( std::is_same< decltype( e.or_else( unexpect32 ).value() ), void >::value, + "or_else mapping to void results in void value" ); + EXPECT( ce.or_else( unexpect32 ).has_value()); + EXPECT( !ue.or_else( unexpect32 ).has_value()); + EXPECT( ue.or_else( make_valid ).has_value() ); + static_assert( std::is_same< decltype( ue.or_else( make_valid ).value() ), void >::value, + "or_else mapping to void results in void value" ); + EXPECT( ue.or_else( unexpect32 ).error() == 32 ); + } +} + +CASE( "expected: transform_error maps unexpected values" ) +{ + const auto mul2 = []( int v ) -> int { return v * 2; }; + enum class my_error { einval }; + const auto map_to_my_error = [](int) { return my_error::einval; }; + + { + expected e; + const expected ce; + expected ue{ unexpect, 42 }; + EXPECT( e.transform_error( mul2 ).has_value()); + EXPECT( ce.transform_error( mul2 ).has_value()); + EXPECT( !ue.transform_error( mul2 ).has_value()); + EXPECT( ue.transform_error( mul2 ).error() == 84 ); + EXPECT( ue.transform_error( map_to_my_error ).error() == my_error::einval ); + } +} + // [expected<> unwrap()] // [expected<> factories] @@ -1932,6 +2126,33 @@ CASE( "issue-58" ) EXPECT( !unexpected.has_value() ); } +CASE( "invoke" ) +{ + struct A { + int x; + constexpr int get() const { return x; } + constexpr int get2(char) const { return x; } + }; + static_assert( nonstd::expected_lite::detail::invoke( &A::x, A{21} ) == 21, "" ); + EXPECT( nonstd::expected_lite::detail::invoke( &MoveOnly::x, MoveOnly(42) ) == 42 ); + constexpr A lval{ 7 }; + static_assert( nonstd::expected_lite::detail::invoke( &A::x, lval ) == 7, "" ); + A mut_lval{ 12 }; + std::reference_wrapper ref{ mut_lval }; + const std::reference_wrapper cref{ lval }; + EXPECT( nonstd::expected_lite::detail::invoke( &A::x, ref ) == 12 ); + EXPECT( nonstd::expected_lite::detail::invoke( &A::x, cref ) == 7 ); + static_assert( nonstd::expected_lite::detail::invoke(&A::x, &lval) == 7, "" ); + static_assert( nonstd::expected_lite::detail::invoke(&A::get, &lval) == 7, "" ); + static_assert( nonstd::expected_lite::detail::invoke(&A::get, A{77}) == 77, "" ); + EXPECT( nonstd::expected_lite::detail::invoke(&A::get, ref) == 12 ); + EXPECT( nonstd::expected_lite::detail::invoke(&A::get, cref) == 7 ); + static_assert( nonstd::expected_lite::detail::invoke(&A::get2, &lval, 'a') == 7, "" ); + static_assert( nonstd::expected_lite::detail::invoke(&A::get2, A{77}, 'a') == 77, "" ); + EXPECT( nonstd::expected_lite::detail::invoke(&A::get2, ref, 'a') == 12 ); + EXPECT( nonstd::expected_lite::detail::invoke(&A::get2, cref, 'a') == 7 ); +} + // ----------------------------------------------------------------------- // using as optional