// This version targets C++11 and later. // // Copyright (c) 2016-2018 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 #include "expected-main.t.hpp" #ifndef nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS #define nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS 0 #endif // Suppress: // - unused parameter, for cases without assertions such as [.std...] #if defined(__clang__) # pragma clang diagnostic ignored "-Wunused-parameter" #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wunused-parameter" #endif //namespace { using namespace nonstd; struct Implicit { int x; Implicit(int v) : x(v) {} }; struct Explicit { int x; explicit Explicit(int v) : x(v) {} }; struct MoveOnly { int x; explicit MoveOnly(int x) :x{x} {} MoveOnly( MoveOnly const & ) = delete; MoveOnly( MoveOnly && other ) noexcept : x{ other.x } {} MoveOnly& operator=( MoveOnly const & ) = delete; MoveOnly& operator=( MoveOnly && other ) noexcept { if (&other == this) return *this; x = other.x; return *this; } }; struct NonMovableNonCopyable { NonMovableNonCopyable() = default; NonMovableNonCopyable( NonMovableNonCopyable const & ) = delete; NonMovableNonCopyable( NonMovableNonCopyable && ) = delete; NonMovableNonCopyable& operator=( NonMovableNonCopyable const & ) = delete; NonMovableNonCopyable& operator=( NonMovableNonCopyable && ) = delete; }; bool operator==( Implicit a, Implicit b ) { return a.x == b.x; } bool operator==( Explicit a, Explicit b ) { return a.x == b.x; } template< typename R > bool operator==( MoveOnly const & a, R const & r ) { return a.x == r; } std::ostream & operator<<( std::ostream & os, Implicit i ) { return os << "Implicit:" << i.x; } std::ostream & operator<<( std::ostream & os, Explicit e ) { return os << "Explicit:" << e.x; } std::ostream & operator<<( std::ostream & os, MoveOnly const & m ) { return os << "MoveOnly:" << m.x; } struct InitList { std::vector vec; char c; InitList( std::initializer_list il, char k ) noexcept : vec( il ), c( k ) {} // InitList( InitList const & ) noexcept = default; // InitList( InitList && ) noexcept = default; // // InitList & operator=( InitList const & ) noexcept = default; // InitList & operator=( InitList && ) noexcept = default; }; enum State { sDefaultConstructed, sValueCopyConstructed, sValueMoveConstructed, sCopyConstructed, sMoveConstructed, sMoveAssigned, sCopyAssigned, sValueCopyAssigned, sValueMoveAssigned, sMovedFrom, sValueConstructed }; struct OracleVal { State s; int i; OracleVal(int i_ = 0) : s(sValueConstructed), i(i_) {} bool operator==( OracleVal const & other ) const { return s==other.s && i==other.i; } }; struct Oracle { State s; OracleVal val; Oracle() : s(sDefaultConstructed) {} Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {} Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;} Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {} Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;} Oracle& operator=(const OracleVal& v) { s = sValueCopyConstructed; val = v; return *this; } Oracle& operator=(OracleVal&& v) { s = sValueMoveConstructed; val = std::move(v); v.s = sMovedFrom; return *this; } Oracle& operator=(const Oracle& o) { s = sCopyConstructed; val = o.val; return *this; } Oracle& operator=(Oracle&& o) { s = sMoveConstructed; val = std::move(o.val); o.s = sMovedFrom; return *this; } bool operator==( Oracle const & other ) const { return s == other.s && val == other.val;} }; std::ostream & operator<<( std::ostream & os, OracleVal const & o ) { using lest::to_string; return os << "[oracle:" << to_string( o.i ) << "]"; } //} // anonymous namespace namespace nonstd { template< typename T, typename E > std::ostream & operator<<( std::ostream & os, expected const & e ) { using lest::to_string; return os << ( e ? to_string( *e ) : "[error:" + to_string( e.error() ) + "]" ); } template< typename E > std::ostream & operator<<( std::ostream & os, unexpected_type const & u ) { using lest::to_string; return os << "[unexp:" << to_string( u.value() ) << "]"; } } using namespace nonstd; // // test specification: // CASE( "A C++11 union can contain non-POD types" "[.]" ) { struct nonpod { nonpod(){} }; union U { char c; nonpod np; }; } // ----------------------------------------------------------------------- // storage_t<> CASE( "[storage_t]" "[.implement]" ) { } // ----------------------------------------------------------------------- // unexpected_type, unexpected_type CASE( "unexpected_type: Disallows default construction" ) { #if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS unexpected_type u; #endif } CASE( "unexpected_type: Allows to copy-construct from unexpected_type, default" ) { unexpected_type a{ 7 }; unexpected_type b( a ); EXPECT( a.error() == 7 ); EXPECT( b.error() == 7 ); } CASE( "unexpected_type: Allows to move-construct from unexpected_type, default" ) { unexpected_type a{ 7 }; unexpected_type b( std::move( a ) ); EXPECT( a.error() == 7 ); EXPECT( b.error() == 7 ); } CASE( "unexpected_type: Allows to in-place-construct" ) { unexpected_type ue( in_place, 5 ); unexpected_type ui( in_place, 7 ); EXPECT( ue.error() == Explicit{5} ); EXPECT( ui.error() == Implicit{7} ); } CASE( "unexpected_type: Allows to in-place-construct from initializer_list" ) { unexpected_type u( in_place, { 7, 8, 9 }, 'a' ); EXPECT( u.error().vec[0] == 7 ); EXPECT( u.error().vec[1] == 8 ); EXPECT( u.error().vec[2] == 9 ); EXPECT( u.error().c == 'a'); } CASE( "unexpected_type: Allows to copy-construct from error_type" ) { unexpected_type u{ 7 }; EXPECT( u.error() == 7 ); } CASE( "unexpected_type: Allows to move-construct from error_type" ) { unexpected_type u{ std::move( 7 ) }; EXPECT( u.error() == 7 ); } CASE( "unexpected_type: Allows to copy-construct from unexpected_type, explicit converting" ) { #if !nsel_USES_STD_EXPECTED // TODO unexpected_type a{ 7 }; unexpected_type b{ a }; EXPECT( b.error() == Explicit{7} ); #else EXPECT( !!"no explicit converting copy-construct for std::unexpected (C++23)." "TODO" ); #endif } CASE( "unexpected_type: Allows to copy-construct from unexpected_type, non-explicit converting" ) { #if !nsel_USES_STD_EXPECTED // TODO unexpected_type a{ 7 }; unexpected_type b( a ); EXPECT( b.error() == Implicit{7} ); #else EXPECT( !!"no non-explicit converting copy-construct for std::unexpected (C++23)." "TODO" ); #endif } CASE( "unexpected_type: Allows to move-construct from unexpected_type, explicit converting" ) { #if !nsel_USES_STD_EXPECTED // TODO unexpected_type a{ 7 }; unexpected_type b{ std::move( a ) }; EXPECT( b.error() == Explicit{7} ); #else EXPECT( !!"no explicit converting move-construct for std::unexpected (C++23)." "TODO" ); #endif } CASE( "unexpected_type: Allows to move-construct from unexpected_type, non-explicit converting" ) { #if !nsel_USES_STD_EXPECTED // TODO unexpected_type a{ 7 }; unexpected_type b( std::move( a ) ); EXPECT( b.error() == Implicit{7} ); #else EXPECT( !!"no non-explicit converting move-construct for std::unexpected (C++23)." "TODO" ); #endif } CASE( "unexpected_type: Allows to copy-assign from unexpected_type, default" ) { unexpected_type a{ 7 }; unexpected_type b{ 0 }; b = a; EXPECT( b.error() == 7 ); } CASE( "unexpected_type: Allows to move-assign from unexpected_type, default" ) { unexpected_type a{ 7 }; unexpected_type b{ 0 }; b = std::move( a ); EXPECT( b.error() == 7 ); } CASE( "unexpected_type: Allows to copy-assign from unexpected_type, converting" ) { #if !nsel_USES_STD_EXPECTED unexpected_type u{ 7 }; unexpected_type ue{ 0 }; unexpected_type ui{ 0 }; ue = u; ui = u; EXPECT( ue.error() == Explicit{7} ); EXPECT( ui.error() == Implicit{7} ); #else EXPECT( !!"no copy-assignment for std::unexpected (C++23)." ); #endif } CASE( "unexpected_type: Allows to move-assign from unexpected, converting" ) { #if !nsel_USES_STD_EXPECTED unexpected_type u{ 7 }; unexpected_type v{ 7 }; unexpected_type ue{ 0 }; unexpected_type ui{ 0 }; ue = std::move( u ); ui = std::move( v ); EXPECT( ue.error() == Explicit{7} ); EXPECT( ui.error() == Implicit{7} ); #else EXPECT( !!"no move-assignment for std::unexpected (C++23)." ); #endif } CASE( "unexpected_type: Allows to observe its value via a l-value reference" ) { unexpected_type u{ 7 }; unexpected_type uc{ 7 }; EXPECT( u.error() == 7 ); EXPECT( uc.error() == 7 ); } CASE( "unexpected_type: Allows to observe its value via a r-value reference" ) { unexpected_type u{ 7 }; unexpected_type uc{ 7 }; EXPECT( std::move( u).error() == 7 ); EXPECT( std::move(uc).error() == 7 ); } CASE( "unexpected_type: Allows to modify its value via a l-value reference" ) { unexpected_type u{ 5 }; u.error() = 7; EXPECT( u.error() == 7 ); } //CASE( "unexpected_type: Allows to modify its value via a r-value reference" ) //{ // const auto v = 9; // unexpected_type u{ 7 }; // // std::move( u.error() ) = v; // // EXPECT( u.error() == v ); //} CASE( "unexpected_type: Allows to be swapped" ) { unexpected_type a{ 5 }; unexpected_type b{ 7 }; a.swap( b ); EXPECT( a.error() == 7 ); EXPECT( b.error() == 5 ); } //CASE( "unexpected_type: Allows reset via = {}" ) //{ // unexpected_type u( 3 ); // // u = {}; //} // ----------------------------------------------------------------------- // unexpected_type // TODO: unexpected_type: Should expected be specialized for particular E types such as exception_ptr and how? // See p0323r7 2.1. Ergonomics, http://wg21.link/p0323 namespace { std::exception_ptr make_ep() { try { // this generates an std::out_of_range: (void) std::string().at(1); } catch(...) { return std::current_exception(); } return nullptr; } } // anonymous namespace CASE( "unexpected_type: Disallows default construction" ) { #if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS unexpected_type u; #endif } CASE( "unexpected_type: Allows to copy-construct from error_type" ) { auto ep = make_ep(); unexpected_type u{ ep }; EXPECT( u.error() == ep ); } CASE( "unexpected_type: Allows to move-construct from error_type" ) { auto ep_move = make_ep(); const auto ep_copy = ep_move; unexpected_type u{ std::move( ep_move ) }; EXPECT( u.error() == ep_copy ); } CASE( "unexpected_type: Allows to copy-construct from an exception" ) { std::string text = "hello, world"; unexpected_type u{ std::make_exception_ptr( std::logic_error( text.c_str() ) ) }; try { std::rethrow_exception( u.error() ); } catch( std::exception const & e ) { EXPECT( e.what() == text ); } } CASE( "unexpected_type: Allows to observe its value" ) { const auto ep = make_ep(); unexpected_type u{ ep }; EXPECT( u.error() == ep ); } CASE( "unexpected_type: Allows to modify its value" ) { const auto ep1 = make_ep(); const auto ep2 = make_ep(); unexpected_type u{ ep1 }; u.error() = ep2; EXPECT( u.error() == ep2 ); } //CASE( "unexpected_type: Allows reset via = {}, std::exception_ptr specialization" ) //{ // unexpected_type u( 3 ); // // u = {}; //} // ----------------------------------------------------------------------- // unexpected_type relational operators CASE( "unexpected_type: Provides relational operators" ) { SETUP( "" ) { unexpected_type u1( 6 ); unexpected_type u2( 7 ); // compare engaged expected with engaged expected SECTION( "==" ) { EXPECT( u1 == u1 ); } SECTION( "!=" ) { EXPECT( u1 != u2 ); } #if nsel_P0323R <= 2 SECTION( "< " ) { EXPECT( u1 < u2 ); } SECTION( "> " ) { EXPECT( u2 > u1 ); } SECTION( "<=" ) { EXPECT( u1 <= u1 ); } SECTION( "<=" ) { EXPECT( u1 <= u2 ); } SECTION( ">=" ) { EXPECT( u1 >= u1 ); } SECTION( ">=" ) { EXPECT( u2 >= u1 ); } #endif } } CASE( "unexpected_type: Provides relational operators, std::exception_ptr specialization" ) { SETUP( "" ) { unexpected_type u( make_ep() ); unexpected_type u2( make_ep() ); // compare engaged expected with engaged expected SECTION( "==" ) { EXPECT ( u == u ); } SECTION( "!=" ) { EXPECT ( u != u2); } #if nsel_P0323R <= 2 SECTION( "< " ) { EXPECT_NOT( u < u ); } SECTION( "> " ) { EXPECT_NOT( u > u ); } SECTION( "< " ) { EXPECT_NOT( u < u2); } SECTION( "> " ) { EXPECT_NOT( u > u2); } SECTION( "<=" ) { EXPECT ( u <= u ); } SECTION( ">=" ) { EXPECT ( u >= u ); } #endif } } // unexpected: traits CASE( "is_unexpected: Is true for unexpected_type" "[.deprecated]" ) { #if nsel_P0323R <= 3 EXPECT( is_unexpected>::value ); #else EXPECT( !!"is_unexpected<> is not available (nsel_P0323R > 3)" ); #endif } CASE( "is_unexpected: Is false for non-unexpected_type (int)" "[.deprecated]" ) { #if nsel_P0323R <= 3 EXPECT_NOT( is_unexpected::value ); #else EXPECT( !!"is_unexpected<> is not available (nsel_P0323R > 3)" ); #endif } // unexpected: factory CASE( "make_unexpected(): Allows to create an unexpected_type from an E" ) { const auto error = 7; auto u = make_unexpected( error ); EXPECT( u.error() == error ); } CASE( "make_unexpected(): Allows to in-place create an unexpected_type from an E" ) { const auto a = 'a'; const auto b = 7; auto u = make_unexpected< std::pair >( in_place, a, b ); EXPECT( u.error().first == a ); EXPECT( u.error().second == b ); } CASE( "make_unexpected_from_current_exception(): Allows to create an unexpected_type from the current exception" "[.deprecated]" ) { #if nsel_P0323R <= 2 std::string text = "hello, world"; try { throw std::logic_error( text.c_str() ); } catch( std::exception const & ) { auto u = make_unexpected_from_current_exception() ; try { std::rethrow_exception( u.error() ); } catch( std::exception const & e ) { EXPECT( e.what() == text ); } } #else EXPECT( !!"make_unexpected_from_current_exception() is not available (nsel_P0323R > 2)" ); #endif } CASE( "unexpected: C++17 and later provide unexpected_type as unexpected" ) { #if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141 nonstd::unexpected u{7}; #else EXPECT( !!"unexpected is not available (no C++17)." ); #endif } // ----------------------------------------------------------------------- // x.x.6 bad_expected_access<> CASE( "bad_expected_access: Disallows default construction" ) { #if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS bad_expected_access bad; #endif } CASE( "bad_expected_access: Allows construction from error_type" ) { bad_expected_access bea( 123 ); } CASE( "bad_expected_access: Allows to observe its error" ) { const int error = 7; bad_expected_access bea( error ); bad_expected_access const beac( error ); EXPECT( bea.error() == error ); EXPECT( beac.error() == error ); EXPECT( std::move( bea.error() ) == error ); EXPECT( std::move( beac.error() ) == error ); } CASE( "bad_expected_access: Allows to change its error" ) { const int old_error = 0; const int new_error = 7; bad_expected_access bea( old_error ); bea.error() = new_error; EXPECT( bea.error() == new_error ); } CASE( "bad_expected_access: Provides non-empty what()" ) { bad_expected_access bea( 123 ); EXPECT( ! std::string( bea.what() ).empty() ); } // ----------------------------------------------------------------------- // expected<> // x.x.4.1 expected<> constructors (value) CASE( "expected: Allows to default construct" ) { expected e; EXPECT( e.has_value() ); } CASE( "expected: Allows to default construct from noncopyable, noncopyable value type" ) { expected e; EXPECT( e.has_value() ); } CASE( "expected: Allows to default construct from noncopyable, noncopyable error type" ) { expected e{ unexpect_t{} }; EXPECT( !e.has_value() ); } CASE( "expected: Allows to copy-construct from expected: value" ) { expected a = 7; expected b{ a }; EXPECT( b ); EXPECT( b.value() == 7 ); } CASE( "expected: Allows to copy-construct from expected: error" ) { expected a{ unexpect, 7 }; expected b{ a }; EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to move-construct from expected: value" ) { expected a = 7; expected b{ std::move( a ) }; EXPECT( b ); EXPECT( b.value() == 7 ); EXPECT( a ); // postcondition: unchanged! } CASE( "expected: Allows to move-construct from expected: error" ) { expected a{ unexpect, 7 }; expected b{ std::move( a ) }; EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to copy-construct from expected; value, explicit converting" ) { expected a = 7; expected b{ a }; EXPECT( b ); EXPECT( b.value() == Explicit{7} ); } CASE( "expected: Allows to copy-construct from expected; error, explicit converting" ) { expected a{ unexpect, 7 }; expected b{ a }; EXPECT( !b ); EXPECT( b.error() == Explicit{7} ); } CASE( "expected: Allows to copy-construct from expected; value, non-explicit converting" ) { expected a = 7; expected b( a ); EXPECT( b ); EXPECT( b.value() == Implicit{7} ); } CASE( "expected: Allows to copy-construct from expected; error, non-explicit converting" ) { expected a{ unexpect, 7 }; expected b( a ); EXPECT( !b ); EXPECT( b.error() == Implicit{7} ); } CASE( "expected: Allows to move-construct from expected; value, explicit converting" ) { expected a = 7; expected b{ std::move( a ) }; EXPECT( b ); EXPECT( b.value() == Explicit{7} ); } CASE( "expected: Allows to move-construct from expected; error, explicit converting" ) { expected a{ unexpect, 7 }; expected b{ std::move( a ) }; EXPECT( !b ); EXPECT( b.error() == Explicit{7} ); } CASE( "expected: Allows to move-construct from expected; value, non-explicit converting" ) { expected a = 7; expected b( std::move( a ) ); EXPECT( b ); EXPECT( b.value() == Implicit{7} ); } CASE( "expected: Allows to move-construct from expected; error, non-explicit converting" ) { expected a{ unexpect, 7 }; expected b( std::move( a ) ); EXPECT( !b ); EXPECT( b.error() == Implicit{7} ); } CASE( "expected: Allows to forward-construct from value, explicit converting" ) { auto v = 7; expected b{ std::move( v ) }; EXPECT( b ); EXPECT( b.value() == Explicit{7} ); } CASE( "expected: Allows to forward-construct from value, non-explicit converting" ) { auto v = 7; expected b{ std::move( v ) }; EXPECT( b ); EXPECT( b.value() == Implicit{7} ); } CASE( "expected: Allows to in-place-construct value" ) { auto v = 7; expected b{ in_place, v }; EXPECT( b ); EXPECT( b.value() == Implicit{7} ); } CASE( "expected: Allows to in-place-construct value from initializer_list" ) { expected b{ in_place, { 7, 8, 9 }, 'a' }; EXPECT( b ); EXPECT( b->vec[0] == 7 ); EXPECT( b->vec[1] == 8 ); EXPECT( b->vec[2] == 9 ); EXPECT( b->c == 'a'); } // x.x.4.1 expected<> constructors (error) CASE( "expected: Allows to copy-construct from unexpected, explicit converting" ) { unexpected_type u{ 7 }; expected e{ u }; EXPECT( e.error() == Explicit{7} ); } CASE( "expected: Allows to copy-construct from unexpected, non-explicit converting" ) { unexpected_type u{ 7 }; expected e{ u }; EXPECT( e.error() == Implicit{7} ); } CASE( "expected: Allows to move-construct from unexpected, explicit converting" ) { unexpected_type u{ 7 }; expected e{ std::move( u ) }; EXPECT( e.error() == Explicit{7} ); } CASE( "expected: Allows to move-construct from unexpected, non-explicit converting" ) { unexpected_type u{ 7 }; expected e{ std::move( u ) }; EXPECT( e.error() == Implicit{7} ); } CASE( "expected: Allows to in-place-construct error" ) { expected e{ unexpect, 7 }; expected i{ unexpect, 7 }; EXPECT( e.error() == Explicit{7} ); EXPECT( i.error() == Implicit{7} ); } CASE( "expected: Allows to in-place-construct error from initializer_list" ) { expected e{ unexpect, { 7, 8, 9 }, 'a' }; EXPECT( !e ); EXPECT( e.error().vec[0] == 7 ); EXPECT( e.error().vec[1] == 8 ); EXPECT( e.error().vec[2] == 9 ); EXPECT( e.error().c == 'a'); } // x.x.4.3 expected<> assignment CASE( "expected: Allows to copy-assign from expected, value" ) { expected a{ 7 }; expected b; b = a; EXPECT( b ); EXPECT( b.value() == 7 ); } CASE( "expected: Allows to copy-assign from expected, error" ) { expected a{ unexpect, 7 }; expected b; b = a; EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to move-assign from expected, value" ) { expected a{ 7 }; expected b; b = std::move( a ); EXPECT( b ); EXPECT( b.value() == 7 ); } CASE( "expected: Allows to move-assign from expected, error" ) { expected a{ unexpect, 7 }; expected b; b = std::move( a ); EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to forward-assign from value" ) { expected a; expected b; a = '7'; b = 7; EXPECT( a ); EXPECT( b ); EXPECT( a.value() == '7' ); EXPECT( b.value() == 7 ); } CASE( "expected: Allows to copy-assign from unexpected" ) { expected e; unexpected_type u{ 7 } ; e = u; EXPECT( e.error() == 7 ); } CASE( "expected: Allows to move-assign from unexpected" ) { expected e; unexpected_type u{ 7 } ; e = std::move( u ); EXPECT( e.error() == 7 ); } CASE( "expected: Allows to move-assign from move-only unexpected" ) { expected e; unexpected_type u{7}; e = std::move( u ); EXPECT( e.error() == 7 ); } CASE( "expected: Allows to emplace value" ) { expected a; expected b; auto va = a.emplace( '7' ); auto vb = b.emplace( 7 ); EXPECT( va == '7' ); EXPECT( vb == 7 ); EXPECT( a.value() == '7' ); EXPECT( b.value() == 7 ); } CASE( "expected: Allows to emplace value from initializer_list" ) { expected e{ in_place, std::initializer_list{}, 'x'}; auto ve = e.emplace( { 7, 8, 9 }, 'a' ); EXPECT( e ); EXPECT( ve.vec[0] == 7 ); EXPECT( ve.vec[1] == 8 ); EXPECT( ve.vec[2] == 9 ); EXPECT( ve.c == 'a'); EXPECT( e.value().vec[0] == 7 ); EXPECT( e.value().vec[1] == 8 ); EXPECT( e.value().vec[2] == 9 ); EXPECT( e.value().c == 'a'); } // x.x.4.4 expected<> swap CASE( "expected: Allows to be swapped" ) { using std::swap; SETUP("") { expected e1{ 1 }; expected e2{ 2 }; expected u1{ unexpect, '1' }; expected u2{ unexpect, '2' }; SECTION("value-value, member swap") { e1.swap( e2 ); EXPECT( e1.value() == 2 ); EXPECT( e2.value() == 1 ); } SECTION("error-error, member swap") { u1.swap( u2 ); EXPECT( u1.error() == '2' ); EXPECT( u2.error() == '1' ); } SECTION("value-error, member swap") { e1.swap( u1 ); EXPECT( e1.error() == '1' ); EXPECT( u1.value() == 1 ); } SECTION("error-value, member swap") { u1.swap( e1 ); EXPECT( u1.value() == 1 ); EXPECT( e1.error() == '1' ); } } } // x.x.4.5 expected<> observers CASE( "expected: Allows to observe its value via a pointer" ) { const auto v = 7; expected e{ v }; EXPECT( e->x == v ); } CASE( "expected: Allows to observe its value via a pointer to constant" ) { const auto v = 7; const expected e{ v }; EXPECT( e->x == v ); } CASE( "expected: Allows to modify its value via a pointer" ) { const auto v1 = 7; const auto v2 = 42; expected e{ v1 }; e->x = v2; EXPECT( e->x == v2 ); } CASE( "expected: Allows to observe its value via a l-value reference" ) { const auto v = 7; expected e{ v }; expected const ec{ v }; EXPECT( *e == Implicit{v} ); EXPECT( *ec == Implicit{v} ); } CASE( "expected: Allows to observe its value via a r-value reference" ) { const auto v = 7; expected e{ v }; expected const ec{ v }; EXPECT( *std::move( e) == Implicit{v} ); EXPECT( *std::move(ec) == Implicit{v} ); } CASE( "expected: Allows to modify its value via a l-value reference" ) { const auto v1 = 7; const auto v2 = 42; expected e{ v1 }; *e = Implicit{v2}; EXPECT( *e == Implicit{v2} ); } CASE( "expected: Allows to modify its value via a r-value reference" ) { const auto v1 = 7; const auto v2 = 42; expected e{ v1 }; #if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490 *std::move(e) = Implicit{v2}; #else *e = Implicit{v2}; // non-r-value #endif EXPECT( *e == Implicit{v2} ); } CASE( "expected: Allows to observe if it contains a value (or error)" ) { expected e; expected u{ unexpect, 3 }; EXPECT( e ); EXPECT( !u ); } CASE( "expected: Allows to observe its value" ) { const auto v = 7; expected e{ v }; expected const ec{ v }; EXPECT( e.value() == Implicit{v} ); EXPECT( ec.value() == Implicit{v} ); } CASE( "expected: Allows to modify its value" ) { const auto v1 = 7; const auto v2 = 42; expected e{ v1 }; e.value() = Implicit{v2}; EXPECT( e.value() == Implicit{v2} ); } CASE( "expected: Allows to move its value" ) { const auto v = 7; expected m{ v }; expected const mc{ v }; expected e{ std::move(m ).value() }; expected ec{ std::move(mc).value() }; EXPECT( e.value() == Implicit{v} ); EXPECT( ec.value() == Implicit{v} ); } CASE( "expected: Allows to observe its error" ) { const auto v = 7; expected e{ unexpect, v }; expected const ec{ unexpect, v }; EXPECT( e.error() == Implicit{v} ); EXPECT( ec.error() == Implicit{v} ); } CASE( "expected: Allows to modify its error" ) { const auto v1 = 7; const auto v2 = 42; expected e{ unexpect, v1 }; e.error() = Implicit{v2}; EXPECT( e.error() == Implicit{v2} ); } CASE( "expected: Allows to move its error" ) { const auto v = 7; expected m{ unexpect, v }; expected const mc{ unexpect, v }; expected e{ unexpect, std::move(m ).error() }; expected ec{ unexpect, std::move(mc).error() }; EXPECT( e.error() == Implicit{v} ); EXPECT( ec.error() == Implicit{v} ); } CASE( "expected: Allows to observe its error as unexpected" ) { #if !nsel_USES_STD_EXPECTED const auto v = 7; expected e{ unexpect, v }; EXPECT( e.get_unexpected().error() == v ); #else EXPECT( !!"expected::get_unexpected() is not available (using C++23 std::expected)" ); #endif } CASE( "expected: Allows to query if it contains an exception of a specific base type" ) { #if !nsel_USES_STD_EXPECTED expected e{ unexpect, std::out_of_range( "oor" ) }; EXPECT( e.has_exception< std::logic_error >() ); EXPECT( !e.has_exception< std::runtime_error >() ); #else EXPECT( !!"expected::has_exception() is not available (using C++23 std::expected)" ); #endif } CASE( "expected: Allows to observe its value if available, or obtain a specified value otherwise" ) { const auto ve = 3; const auto vu = 7; expected e{ ve }; expected u{ unexpect, 0 }; EXPECT( e.value_or( vu ) == ve ); EXPECT( u.value_or( vu ) == vu ); } CASE( "expected: Allows to move its value if available, or obtain a specified value otherwise" ) { const auto ve = 3; const auto vu = 7; expected mv{ ve }; expected mu{ unexpect, 0 }; EXPECT( std::move( mv ).value_or( vu ) == ve ); EXPECT( std::move( mu ).value_or( vu ) == vu ); } CASE( "expected: Throws bad_expected_access on value access when disengaged" ) { expected e{ unexpect, 7 }; expected ec{ unexpect, 7 }; EXPECT_THROWS( e.value() ); EXPECT_THROWS_AS( e.value(), bad_expected_access ); EXPECT_THROWS( ec.value() ); EXPECT_THROWS_AS( ec.value(), bad_expected_access ); EXPECT_THROWS( std::move( e).value() ); EXPECT_THROWS_AS( std::move( e).value(), bad_expected_access ); EXPECT_THROWS( std::move(ec).value() ); EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access ); } #if nsel_P2505R >= 4 CASE( "expected: Allows to observe its unexpected value, or fallback to the specified value with error_or" " [monadic p2505r4]") { const auto ve = 3; const auto vu = 7; expected e{ ve }; expected u{ unexpect, 0 }; EXPECT( e.error_or( vu ) == vu ); EXPECT( u.error_or( vu ) == 0 ); } #endif // nsel_P2505R >= 4 #if nsel_P2505R >= 3 CASE( "expected: Allows to map value with and_then" " [monadic p2505r3]" ) { 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: Allows to map unexpected with or_else" " [monadic p2505r3]" ) { 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: Allows to transform value" " [monadic p2505r3]" ) { 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 ); } #if nsel_P2505R >= 5 // R5 changed remove_cvref_t to remove_cv_t in transform/transform_error, which broke data member pointer transforms, // because the result type must be a valid expected value type, but expressions with reference types are not. const auto moveonly_map_to_x = [](MoveOnly val) { return val.x; }; #else const auto moveonly_map_to_x = &MoveOnly::x; #endif // nsel_P2505R >= 5 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: Allows to map errors with transform_error" " [monadic p2505r3]" ) { 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 ); } } #endif // nsel_P2505R >= 3 // ----------------------------------------------------------------------- // expected specialization // x.x.4.1 expected constructors CASE( "expected: Allows to default-construct" ) { expected e; EXPECT( e.has_value() ); } CASE( "expected: Allows to copy-construct from expected: value" ) { expected a; expected b{ a }; EXPECT( b ); } CASE( "expected: Allows to copy-construct from expected: error" ) { expected a{ unexpect, 7 }; expected b{ a }; EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to move-construct from expected: value" ) { expected a; expected b{ std::move( a ) }; EXPECT( b ); EXPECT( a ); // postcondition: unchanged! } CASE( "expected: Allows to move-construct from expected: error" ) { expected a{ unexpect, 7 }; expected b{ std::move( a ) }; EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to in-place-construct" ) { expected e{ in_place }; EXPECT( e ); } CASE( "expected: Allows to copy-construct from unexpected, explicit converting" ) { unexpected_type u{ 7 }; expected e{ u }; EXPECT( e.error() == Explicit{7} ); } CASE( "expected: Allows to copy-construct from unexpected, non-explicit converting" ) { unexpected_type u{ 7 }; expected e{ u }; EXPECT( e.error() == Implicit{7} ); } CASE( "expected: Allows to move-construct from unexpected, explicit converting" ) { unexpected_type u{ 7 }; expected e{ std::move( u ) }; EXPECT( e.error() == Explicit{7} ); } CASE( "expected: Allows to move-construct from unexpected, non-explicit converting" ) { unexpected_type u{ 7 }; expected e{ std::move( u ) }; EXPECT( e.error() == Implicit{7} ); } CASE( "expected: Allows to in-place-construct unexpected_type" ) { expected e{ unexpect, 7 }; expected i{ unexpect, 7 }; EXPECT( e.error() == Explicit{7} ); EXPECT( i.error() == Implicit{7} ); } CASE( "expected: Allows to in-place-construct error from initializer_list" ) { expected e{ unexpect, { 7, 8, 9 }, 'a' }; EXPECT( !e ); EXPECT( e.error().vec[0] == 7 ); EXPECT( e.error().vec[1] == 8 ); EXPECT( e.error().vec[2] == 9 ); EXPECT( e.error().c == 'a'); } // x.x.4.3 expected assignment CASE( "expected: Allows to copy-assign from expected, value" ) { expected a; expected b; b = a; EXPECT( b ); } CASE( "expected: Allows to copy-assign from expected, error" ) { expected a{ unexpect, 7 }; expected b; b = a; EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to move-assign from expected, value" ) { expected a; expected b; b = std::move( a ); EXPECT( b ); } CASE( "expected: Allows to move-assign from expected, error" ) { expected a{ unexpect, 7 }; expected b; b = std::move( a ); EXPECT( !b ); EXPECT( b.error() == 7 ); } CASE( "expected: Allows to emplace value" ) { expected a{ unexpect, 7 }; a.emplace(); EXPECT( a ); } // x.x.4.4 expected swap CASE( "expected: Allows to be swapped" ) { using std::swap; SETUP("") { expected e1; expected e2; expected u1{ unexpect, '1' }; expected u2{ unexpect, '2' }; SECTION("value-value, member swap") { e1.swap( e2 ); EXPECT( e1 ); EXPECT( e2 ); } SECTION("error-error, member swap") { u1.swap( u2 ); EXPECT( u1.error() == '2' ); EXPECT( u2.error() == '1' ); } SECTION("value-error, member swap") { e1.swap( u1 ); EXPECT( e1.error() == '1' ); EXPECT( u1 ); } SECTION("error-value, member swap") { u1.swap( e1 ); EXPECT( u1 ); EXPECT( e1.error() == '1' ); } } } // x.x.4.5 expected observers CASE( "expected: Allows to observe if it contains a value (or error)" ) { expected e; expected u{ unexpect, 3 }; EXPECT( e ); EXPECT( !u ); } CASE( "expected: Allows to observe its value" ) { expected e; expected const ec; EXPECT( ( e.value(), true ) ); EXPECT( ( ec.value(), true ) ); } CASE( "expected: Allows to observe its error" ) { const auto v = 7; expected e{ unexpect, v }; expected const ec{ unexpect, v }; EXPECT( e.error() == Implicit{v} ); EXPECT( ec.error() == Implicit{v} ); } CASE( "expected: Allows to modify its error" ) { const auto v1 = 7; const auto v2 = 42; expected e{ unexpect, v1 }; e.error() = Implicit{v2}; EXPECT( e.error() == Implicit{v2} ); } CASE( "expected: Allows to move its error" ) { const auto v = 7; expected m{ unexpect, v }; expected const mc{ unexpect, v }; expected e{ unexpect, std::move(m ).error() }; expected ec{ unexpect, std::move(mc).error() }; EXPECT( e.error() == Implicit{v} ); EXPECT( ec.error() == Implicit{v} ); } CASE( "expected: Allows to observe its error as unexpected" ) { #if !nsel_USES_STD_EXPECTED const auto value = 7; expected e{ unexpect, value }; EXPECT( e.get_unexpected().error() == value ); #else EXPECT( !!"expected::get_unexpected() is not available (using C++23 std::expected)" ); #endif } CASE( "expected: Allows to query if it contains an exception of a specific base type" ) { #if !nsel_USES_STD_EXPECTED expected e{ unexpect, std::out_of_range( "oor" ) }; EXPECT( e.has_exception< std::logic_error >() ); EXPECT( !e.has_exception< std::runtime_error >() ); #else EXPECT( !!"expected::has_exception() is not available (using C++23 std::expected)" ); #endif } CASE( "expected: Throws bad_expected_access on value access when disengaged" ) { expected e{ unexpect, 7 }; expected ec{ unexpect, 7 }; EXPECT_THROWS( e.value() ); EXPECT_THROWS_AS( e.value(), bad_expected_access ); EXPECT_THROWS( ec.value() ); EXPECT_THROWS_AS( ec.value(), bad_expected_access ); EXPECT_THROWS( std::move( e).value() ); EXPECT_THROWS_AS( std::move( e).value(), bad_expected_access ); EXPECT_THROWS( std::move(ec).value() ); EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access ); } #if nsel_P2505R >= 4 CASE( "expected: Allows to observe unexpected value, or fallback to a default value with error_or" " [monadic p2505r4]" ) { const auto vu = 7; expected e; expected u{ unexpect, 0 }; EXPECT( e.error_or( vu ) == vu ); EXPECT( u.error_or( vu ) == 0 ); } #endif // nsel_P2505R >= 4 #if nsel_P2505R >= 3 CASE( "expected: Allows to call argless functions with and_then" " [monadic p2505r3]" ) { 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: Allows to map to expected or unexpected with or_else" " [monadic p2505r3]" ) { 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: Allows to assign a new expected value using transform" " [monadic p2505r3]" ) { const auto make_int_32 = [] { return 32; }; const auto mul2 = [](int v) { return v * 2; }; expected e; static_assert( std::is_same< decltype( e.transform( make_int_32 ) )::value_type, int >::value, "" ); EXPECT( e.transform( make_int_32 ).value() == 32 ); EXPECT( e.transform( make_int_32 ).transform( mul2 ).value() == 64 ); } CASE( "expected: Allows to map unexpected error value via transform_error" " [monadic p2505r3]" ) { 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 ); } } #endif // nsel_P2505R >= 3 // [expected<> unwrap()] // [expected<> factories] // x.x.4.7 expected<>: relational operators CASE( "operators: Provides expected relational operators" ) { SETUP( "" ) { auto v1 = 6; auto v2 = 7; expected e1( v1 ); expected e2( v2 ); unexpected_type u( 'u' ); expected d( u ); // compare engaged expected with engaged expected SECTION( "engaged == engaged" ) { EXPECT( e1 == e1 ); } SECTION( "engaged != engaged" ) { EXPECT( e1 != e2 ); } #if nsel_P0323R <= 2 SECTION( "engaged < engaged" ) { EXPECT( e1 < e2 ); } SECTION( "engaged > engaged" ) { EXPECT( e2 > e1 ); } SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e1 ); } SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e2 ); } SECTION( "engaged >= engaged" ) { EXPECT( e1 >= e1 ); } SECTION( "engaged >= engaged" ) { EXPECT( e2 >= e1 ); } #endif // compare engaged expected with value SECTION( "engaged == value" ) { EXPECT( e1 == v1 ); } SECTION( "value == engaged" ) { EXPECT( v1 == e1 ); } SECTION( "engaged != value" ) { EXPECT( e1 != v2 ); } SECTION( "value != engaged" ) { EXPECT( v1 != e2 ); } #if nsel_P0323R <= 2 SECTION( "engaged < value" ) { EXPECT( e1 < v2 ); } SECTION( "value < engaged" ) { EXPECT( v1 < e2 ); } SECTION( "engaged > value" ) { EXPECT( e2 > v1 ); } SECTION( "value > engaged" ) { EXPECT( v2 > e1 ); } SECTION( "engaged <= value" ) { EXPECT( e1 <= v2 ); } SECTION( "value <= engaged" ) { EXPECT( v1 <= e2 ); } SECTION( "engaged >= value" ) { EXPECT( e2 >= v1 ); } SECTION( "value >= engaged" ) { EXPECT( v2 >= e1 ); } #endif // compare engaged expected with disengaged expected SECTION( "engaged == disengaged" ) { EXPECT_NOT( e1 == d ); } SECTION( "disengaged == engaged" ) { EXPECT_NOT( d == e1 ); } SECTION( "engaged != disengaged" ) { EXPECT ( e1 != d ); } SECTION( "disengaged != engaged" ) { EXPECT ( d != e2 ); } #if nsel_P0323R <= 2 SECTION( "engaged < disengaged" ) { EXPECT_NOT( e1 < d ); } SECTION( "disengaged < engaged" ) { EXPECT ( d < e2 ); } SECTION( "engaged > disengaged" ) { EXPECT ( e2 > d ); } SECTION( "disengaged > engaged" ) { EXPECT_NOT( d > e1 ); } SECTION( "engaged <= disengaged" ) { EXPECT_NOT( e1 <= d ); } SECTION( "disengaged <= engaged" ) { EXPECT ( d <= e2 ); } SECTION( "engaged >= disengaged" ) { EXPECT ( e2 >= d ); } SECTION( "disengaged >= engaged" ) { EXPECT_NOT( d >= e1 ); } #endif // compare engaged expected with unexpected SECTION( "disengaged == unexpected" ) { EXPECT ( d == u ); } SECTION( "unexpected == disengaged" ) { EXPECT ( u == d ); } SECTION( "engaged != unexpected" ) { EXPECT ( e1 != u ); } SECTION( "unexpected != engaged" ) { EXPECT ( u != e1 ); } #if nsel_P0323R <= 2 SECTION( "disengaged < unexpected" ) { EXPECT_NOT( d < u ); } SECTION( "unexpected < disengaged" ) { EXPECT_NOT( u < d ); } SECTION( "disengaged <= unexpected" ) { EXPECT ( d <= u ); } SECTION( "unexpected <= disengaged" ) { EXPECT ( u <= d ); } SECTION( "disengaged > unexpected" ) { EXPECT_NOT( d > u ); } SECTION( "unexpected > disengaged" ) { EXPECT_NOT( u > d ); } SECTION( "disengaged >= unexpected" ) { EXPECT ( d >= u ); } SECTION( "unexpected >= disengaged" ) { EXPECT ( u >= d ); } #endif } } CASE( "operators: Provides expected relational operators (void)" ) { SETUP( "" ) { expected ev1; expected ev2; expected evu{ unexpect }; // compare engaged expected with engaged expected SECTION( " engaged == engaged" ) { EXPECT( ev1 == ev2 ); } SECTION( "!(engaged != engaged)" ) { EXPECT_NOT( ev1 != ev2 ); } // compare engaged expected with disengaged expected SECTION( " engaged != disengaged" ) { EXPECT( ev1 != evu ); } SECTION( "!(engaged == disengaged)" ) { EXPECT_NOT( ev1 == evu ); } } } // ----------------------------------------------------------------------- // expected: specialized algorithms // ----------------------------------------------------------------------- // Other CASE( "swap: Allows expected to be swapped" ) { using std::swap; SETUP("") { expected e1{ 1 }; expected e2{ 2 }; expected u1{ unexpect, '1' }; expected u2{ unexpect, '2' }; SECTION("value-value, std::swap") { swap( e1, e2 ); EXPECT( e1.value() == 2 ); EXPECT( e2.value() == 1 ); } SECTION("error-error, std::swap") { swap( u1, u2 ); EXPECT( u1.error() == '2' ); EXPECT( u2.error() == '1' ); } SECTION("value-error, std::swap") { swap( e1, u1 ); EXPECT( e1.error() == '1' ); EXPECT( u1.value() == 1 ); } SECTION("error-value, std::swap") { swap( u1, e1 ); EXPECT( u1.value() == 1 ); EXPECT( e1.error() == '1' ); } } } CASE( "std::hash: Allows to compute hash value for expected" ) { #if !nsel_USES_STD_EXPECTED expected a{ 7 }; expected b{ 7 }; EXPECT( (std::hash< expected >{}( a )) == (std::hash< expected >{}( b )) ); #else EXPECT( !!"std::hash> is not available for std::unexpected (C++23)." ); #endif } #if nsel_P0323R <= 3 #include void vfoo() {} expected foo() { return make_expected( 7 ); } expected> bar() { return make_expected( std::unique_ptr( new int(7) ) ); } #endif // nsel_P0323R CASE( "make_expected(): create expected from given value" "[.deprecated]" ) { #if nsel_P0323R <= 3 auto e = make_expected( 7 ); EXPECT( e ); EXPECT( *e == 7 ); #else EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" ); #endif } CASE( "make_expected(): create expected" "[.deprecated]" ) { #if nsel_P0323R <= 3 auto e = make_expected(); EXPECT( e ); #else EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" ); #endif } CASE( "make_expected_from_current_exception(): create expected from current exception" "[.deprecated]" ) { #if nsel_P0323R <= 3 EXPECT( !!"Implement" ); #else EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" ); #endif } CASE( "make_expected_from_exception(): create expected from given exception" "[.deprecated]" ) { #if nsel_P0323R <= 3 EXPECT( !!"Implement" ); #else EXPECT( !!"make_expected() is not available (nsel_P0323R > 3)" ); #endif } CASE( "make_expected_from_call(): non-void return type" "[.deprecated]" ) { #if nsel_P0323R <= 3 expected ei = foo(); expected> eup = bar(); auto e2 = make_expected_from_call( foo ); auto eup2 = make_expected_from_call( bar ); EXPECT( e2 ); EXPECT( eup2 ); #else EXPECT( !!"make_expected_from_call() is not available (nsel_P0323R > 3)" ); #endif } CASE( "make_expected_from_call(): void return type" "[.deprecated]" ) { #if nsel_P0323R <= 3 auto ev = make_expected_from_call( vfoo ); EXPECT( ev ); #else EXPECT( !!"make_expected_from_call() is not available (nsel_P0323R > 3)" ); #endif } CASE( "tweak header: reads tweak header if supported " "[tweak]" ) { #if expected_HAVE_TWEAK_HEADER EXPECT( EXPECTED_TWEAK_VALUE == 42 ); #else EXPECT( !!"Tweak header is not available (expected_HAVE_TWEAK_HEADER: 0)." ); #endif } // ----------------------------------------------------------------------- // expected: issues // issue #15, https://github.com/martinmoene/expected-dark/issues/15 CASE( "issue-15d" ) { nonstd::expected< int, std::error_code > e = 12; (void)e.value(); } // issue #15, https://github.com/martinmoene/expected-lite/issues/15 CASE( "issue-15" ) { (void) nonstd::expected< int, int >( 12).value(); } // issue #29, https://github.com/martinmoene/expected-lite/issues/29 // issue #32, https://github.com/martinmoene/expected-lite/issues/32 namespace issue_32 { enum class Error { Bad }; class MyNonMoveableObject { public: MyNonMoveableObject() = default; MyNonMoveableObject( MyNonMoveableObject const & ) = default; MyNonMoveableObject( MyNonMoveableObject && ) = delete; MyNonMoveableObject& operator=( MyNonMoveableObject const &) = default; MyNonMoveableObject& operator=( MyNonMoveableObject &&) = delete; ~MyNonMoveableObject() = default; }; nonstd::expected< MyNonMoveableObject, Error > create_copyable() { return nonstd::expected< MyNonMoveableObject, Error >{}; } class MyNonCopyableObject { public: MyNonCopyableObject() = default; MyNonCopyableObject( MyNonCopyableObject const & ) = delete; MyNonCopyableObject( MyNonCopyableObject && ) = default; MyNonCopyableObject& operator=( MyNonCopyableObject const & ) = delete; MyNonCopyableObject& operator=( MyNonCopyableObject && ) = default; ~MyNonCopyableObject() = default; }; nonstd::expected< MyNonCopyableObject, Error > create_moveable() { return MyNonCopyableObject{}; } } // namespace issue_32 CASE( "pr-41" ) { expected a{7}; const expected ca{7}; expected b{unexpect, 7}; const expected cb{unexpect, 7}; expected c{unexpect, 7}; const expected cc{unexpect, 7}; EXPECT( *std::move(a) == 7 ); EXPECT( *std::move(ca) == 7 ); EXPECT( std::move(b).error() == 7 ); EXPECT( std::move(cb).error() == 7 ); EXPECT( std::move(c).error() == 7 ); EXPECT( std::move(cc).error() == 7 ); } // issue #50, https://github.com/martinmoene/expected-lite/issues/50 namespace issue_50 { struct MyConstMemberNonMoveableObject { const int x; MyConstMemberNonMoveableObject( int x_ ) : x( x_ ) {} MyConstMemberNonMoveableObject( MyConstMemberNonMoveableObject const & ) = default; }; nonstd::unexpected_type create_nonmoveable() { return nonstd::make_unexpected( MyConstMemberNonMoveableObject(3) ); } } // namespace issue_50 namespace issue_51 { int compare_equal_with_expected_void() { auto ev1 = nonstd::expected{}; auto ev2 = nonstd::expected{}; return ev1 == ev2; } int compare_not_equal_with_expected_void() { auto ev1 = nonstd::expected{}; auto ev2 = nonstd::expected{}; return ev1 != ev2; } } // namespace issue_51 namespace issue_59 { struct NonMovableNonCopyable { NonMovableNonCopyable() = default; NonMovableNonCopyable( NonMovableNonCopyable const & ) = delete; NonMovableNonCopyable( NonMovableNonCopyable && ) = delete; NonMovableNonCopyable& operator=( NonMovableNonCopyable const & ) = delete; NonMovableNonCopyable& operator=( NonMovableNonCopyable && ) = delete; }; } // namespace issue_59 CASE( "issue-58" ) { static_assert( !std::is_copy_constructible::value, "is not copy constructible" ); static_assert( !std::is_move_constructible::value, "is not move constructible" ); nonstd::expected expected; nonstd::expected unexpected( nonstd::unexpect_t{} ); EXPECT( expected.has_value() ); EXPECT( !unexpected.has_value() ); } #if !nsel_USES_STD_EXPECTED && nsel_P2505R >= 3 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 ); } #endif // nsel_USES_STD_EXPECTED && nsel_P2505R >= 3 // ----------------------------------------------------------------------- // using as optional #if 1 /// disengaged optional state tag struct nullopt_t{}; const nullopt_t nullopt{}; /// optional expressed in expected template< typename T > using optional = expected; #endif