// // Copyright 2014-2018 by Martin Moene // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // optional lite is inspired on std::optional by Fernando Cacciola and Andrzej Krzemienski // and on expected lite by Martin Moene. #include "optional-main.t.hpp" using namespace nonstd; #if optional_USES_STD_OPTIONAL && defined(__APPLE__) # define opt_value( o ) *o #else # define opt_value( o ) o.value() #endif namespace { struct nonpod { nonpod(){} }; struct Implicit { int x; Implicit(int v) : x(v) {} }; struct Explicit { int x; explicit Explicit(int v) : x(v) {} }; bool operator==( Implicit a, Implicit b ) { return a.x == b.x; } bool operator==( Explicit a, Explicit b ) { return a.x == b.x; } 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; } // ensure comparison of pointers for lest: // const void * lest_nullptr = 0; // The following tracer code originates as Oracle from Optional by // Andrzej Krzemienski, https://github.com/akrzemi1/Optional. enum State { /* 0 */ default_constructed, /* 1 */ value_copy_constructed, /* 2 */ value_move_constructed, /* 3 */ copy_constructed, /* 4 */ move_constructed, /* 5 */ move_assigned, /* 6 */ copy_assigned, /* 7 */ value_copy_assigned, /* 8 */ value_move_assigned, /* 9 */ moved_from, /*10 */ value_constructed }; struct V { State state; int value; V( ) : state( default_constructed ), value( deflt() ) {} V( int v ) : state( value_constructed ), value( v ) {} bool operator==( V const & rhs ) const { return state == rhs.state && value == rhs.value; } bool operator==( int val ) const { return value == val; } static int deflt() { return 42; } }; struct S { State state; V value; S( ) : state( default_constructed ) {} S( V const & v ) : state( value_copy_constructed ), value( v ) {} S( S const & s ) : state( copy_constructed ), value( s.value ) {} S & operator=( V const & v ) { state = value_copy_assigned; value = v; return *this; } S & operator=( const S & s ) { state = copy_assigned ; value = s.value; return *this; } #if optional_CPP11_OR_GREATER S( V && v ) : state( value_move_constructed ), value( std::move( v ) ) { v.state = moved_from; } S( S && s ) : state( move_constructed ), value( std::move( s.value ) ) { s.state = moved_from; } S & operator=( V && v ) { state = value_move_assigned ; value = std::move( v ); v.state = moved_from; return *this; } S & operator=( S && s ) { state = move_assigned ; value = std::move( s.value ); s.state = moved_from; return *this; } #endif bool operator==( S const & rhs ) const { return state == rhs.state && value == rhs.value; } }; inline std::ostream & operator<<( std::ostream & os, V const & v ) { using lest::to_string; return os << "[V:" << to_string( v.value ) << "]"; } inline std::ostream & operator<<( std::ostream & os, S const & s ) { using lest::to_string; return os << "[S:" << to_string( s.value ) << "]"; } struct NoDefault { NoDefault( NoDefault const & ) {} NoDefault & operator=( NoDefault const & ) { return *this; } #if optional_CPP11_OR_GREATER NoDefault( NoDefault && ) = default; NoDefault & operator=( NoDefault && ) = default; #endif private: NoDefault(); }; struct CopyOnly { CopyOnly( CopyOnly const & ) {} CopyOnly & operator=( CopyOnly const & ) { return *this; } private: CopyOnly(); #if optional_CPP11_OR_GREATER CopyOnly( CopyOnly && ) = delete; CopyOnly & operator=( CopyOnly && ) = delete; #endif }; struct MoveOnly { #if optional_CPP11_OR_GREATER MoveOnly( MoveOnly && ) = default; MoveOnly & operator=( MoveOnly && ) = default; #endif private: MoveOnly(); MoveOnly( MoveOnly const & ); MoveOnly & operator=( MoveOnly const & ); }; struct NoDefaultCopyMove { std::string text; NoDefaultCopyMove( std::string txt ) : text( txt ) {} private: NoDefaultCopyMove(); NoDefaultCopyMove( NoDefaultCopyMove const & ); NoDefaultCopyMove & operator=( NoDefaultCopyMove const & ); #if optional_CPP11_OR_GREATER NoDefaultCopyMove( NoDefaultCopyMove && ) = delete; NoDefaultCopyMove & operator=( NoDefaultCopyMove && ) = delete; #endif }; #if optional_CPP11_OR_GREATER struct InitList { std::vector vec; char c; S s; InitList( std::initializer_list il, char k, S const & t ) : vec( il ), c( k ), s( t ) {} InitList( std::initializer_list il, char k, S && t ) : vec( il ), c( k ), s( std::move( t ) ) {} }; #endif } // anonymous namespace // // test specification: // CASE( "union: A C++03 union can only contain POD types" ) { union U { char c; #if optional_CPP11_OR_GREATER nonpod np; #endif }; } // // optional member operations: // // construction: CASE( "optional: Allows to default construct an empty optional (1a)" ) { optional a; EXPECT( !a ); } CASE( "optional: Allows to explicitly construct a disengaged, empty optional via nullopt (1b)" ) { optional a( nullopt ); EXPECT( !a ); } CASE( "optional: Allows to default construct an empty optional with a non-default-constructible (1a)" ) { // FAILS: NoDefault nd; // FAILS: NoDefaultCopyMove ndcm; optional ond; optional oco; optional omo; optional ondcm; EXPECT( !ond ); EXPECT( !oco ); EXPECT( !omo ); EXPECT( !ondcm ); } CASE( "optional: Allows to copy-construct from empty optional (2)" ) { optional a; optional b( a ); EXPECT( !b ); } CASE( "optional: Allows to move-construct from empty optional (C++11, 3)" ) { #if optional_CPP11_OR_GREATER optional a; optional b( std::move( a ) ); EXPECT( !b ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-construct from empty optional, explicit converting (C++11, 4a)" ) { #if optional_CPP11_OR_GREATER optional a; optional b{ a }; EXPECT( !b ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-construct from empty optional, non-explicit converting (4b)" ) { optional a; optional b( a ); EXPECT( !b ); } CASE( "optional: Allows to move-construct from empty optional, explicit converting (C++11, 5a)" ) { #if optional_CPP11_OR_GREATER optional a; optional b{ std::move( a ) }; EXPECT( !b ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to move-construct from empty optional, non-explicit converting (C++11, 5a)" ) { #if optional_CPP11_OR_GREATER optional a; optional b( std::move( a ) ); EXPECT( !b ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-construct from non-empty optional (2)" ) { optional a( 7 ); optional b( a ); EXPECT( b ); EXPECT( *b == 7 ); } CASE( "optional: Allows to copy-construct from non-empty optional, explicit converting (C++11, 4a)" ) { #if optional_CPP11_OR_GREATER optional a( 7 ); optional b{ a }; EXPECT( b ); EXPECT( *b == Explicit{7} ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-construct from non-empty optional, non-explicit converting (4b)" ) { optional a( 7 ); optional b( a ); EXPECT( b ); EXPECT( *b == Implicit(7) ); } CASE( "optional: Allows to move-construct from non-empty optional (C++11, 3)" ) { #if optional_CPP11_OR_GREATER optional a( 7 ); optional b( std::move( a ) ); EXPECT( b ); EXPECT( *b == 7 ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to move-construct from non-empty optional, explicit converting (C++11, 5a)" ) { #if optional_CPP11_OR_GREATER optional a( 7 ); optional b{ std::move( a ) }; EXPECT( b ); EXPECT( *b == Explicit{7} ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to move-construct from non-empty optional, non-explicit converting (C++11, 5b)" ) { #if optional_CPP11_OR_GREATER optional a( 7 ); optional b( std::move( a ) ); EXPECT( b ); EXPECT( *b == Implicit(7) ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } namespace { #if optional_CPP11_OR_GREATER void use_optional( nonstd::optional ) {} #else template< typename T > void use_optional( T ) {} #endif } CASE( "optional: Allows to copy-construct from literal value (8)" ) { use_optional( 7 ); optional a = 7; EXPECT( a ); EXPECT( *a == 7 ); } CASE( "optional: Allows to copy-construct from literal value, converting (8)" ) { use_optional( '7' ); optional a = '7'; EXPECT( a ); EXPECT( *a == '7' ); } CASE( "optional: Allows to copy-construct from value (8)" ) { const int i = 7; use_optional( i ); optional a( i ); EXPECT( a ); EXPECT( *a == 7 ); } CASE( "optional: Allows to copy-construct from value, converting (8)" ) { const char c = '7'; use_optional( c ); optional a( c ); EXPECT( a ); EXPECT( *a == '7' ); } CASE( "optional: Allows to move-construct from value (C++11, 8b)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); optional a( std::move( s ) ); EXPECT( a->value == 7 ); EXPECT( a->state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to move-construct from value, explicit converting (C++11, 8a)" ) { #if optional_CPP11_OR_GREATER int seven = 7; optional a{ std::move( seven ) }; EXPECT( a ); EXPECT( *a == Explicit{7} ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to move-construct from value, non-explicit converting (C++11, 8b)" ) { #if optional_CPP11_OR_GREATER int seven = 7; optional a( std::move( seven ) ); EXPECT( a ); EXPECT( *a == Implicit(7) ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to in-place construct from literal value (C++11, 6)" ) { #if optional_CPP11_OR_GREATER using pair_t = std::pair; optional a( in_place, 'a', 7 ); EXPECT( a->first == 'a' ); EXPECT( a->second == 7 ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to in-place copy-construct from value (C++11, 6)" ) { #if optional_CPP11_OR_GREATER char c = 'a'; S s( 7 ); using pair_t = std::pair; optional a( in_place, c, s ); EXPECT( a->first == 'a' ); EXPECT( a->second.value == 7 ); #if optional_USES_STD_OPTIONAL EXPECT( a->second.state == copy_constructed ); #else EXPECT( a->second.state == move_constructed ); #endif EXPECT( s.state != moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to in-place move-construct from value (C++11, 6)" ) { #if optional_CPP11_OR_GREATER char c = 'a'; S s( 7 ); using pair_t = std::pair; optional a( in_place, c, std::move( s ) ); EXPECT( a->first == 'a' ); EXPECT( a->second.value == 7 ); EXPECT( a->second.state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to in-place copy-construct from initializer-list (C++11, 7)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); optional a( in_place, { 7, 8, 9, }, 'a', s ); EXPECT( a->vec[0] == 7 ); EXPECT( a->vec[1] == 8 ); EXPECT( a->vec[2] == 9 ); EXPECT( a->c == 'a'); EXPECT( a->s.value == 7 ); #if optional_USES_STD_OPTIONAL EXPECT( a->s.state == copy_constructed ); #else EXPECT( a->s.state == move_constructed ); #endif EXPECT( s.state != moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to in-place move-construct from initializer-list (C++11, 7)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); optional a( in_place, { 7, 8, 9, }, 'a', std::move( s ) ); EXPECT( a->vec[0] == 7 ); EXPECT( a->vec[1] == 8 ); EXPECT( a->vec[2] == 9 ); EXPECT( a->c == 'a' ); EXPECT( a->s.value == 7 ); EXPECT( a->s.state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } // assignment: CASE( "optional: Allows to assign nullopt to disengage (1)" ) { optional a( 7 ); a = nullopt; EXPECT( !a ); } CASE( "optional: Allows to copy-assign from/to engaged and disengaged optionals (2)" ) { SETUP( "" ) { optional d1; optional d2; optional e1( 123 ); optional e2( 987 ); SECTION( "a disengaged optional assigned nullopt remains empty" ) { d1 = nullopt; EXPECT( !d1 ); } SECTION( "a disengaged optional assigned an engaged optional obtains its value" ) { d1 = e1; EXPECT( d1 ); EXPECT( *d1 == 123 ); } SECTION( "an engaged optional assigned an engaged optional obtains its value" ) { e1 = e2; EXPECT( e1 ); EXPECT( *e1 == 987 ); } SECTION( "an engaged optional assigned nullopt becomes empty" ) { e1 = nullopt; EXPECT( !e1 ); } SECTION( "a disengaged optional assigned a disengaged optional remains empty" ) { d1 = d2; EXPECT( !d1 ); }} } CASE( "optional: Allows to move-assign from/to engaged and disengaged optionals (C++11, 3)" ) { #if optional_CPP11_OR_GREATER SETUP( "" ) { optional d1; optional d2; optional e1( 123 ); optional e2( 987 ); SECTION( "a disengaged optional assigned nullopt remains empty" ) { d1 = std::move( nullopt ); EXPECT( !d1 ); } SECTION( "a disengaged optional assigned an engaged optional obtains its value" ) { d1 = std::move( e1); EXPECT( d1 ); EXPECT( *d1 == 123 ); } SECTION( "an engaged optional assigned an engaged optional obtains its value" ) { e1 = std::move( e2 ); EXPECT( e1 ); EXPECT( *e1 == 987 ); } SECTION( "an engaged optional assigned nullopt becomes empty" ) { e1 = std::move( nullopt ); EXPECT( !e1 ); } SECTION( "a disengaged optional assigned a disengaged optional remains empty" ) { d1 = std::move( d2); EXPECT( !d1 ); }} #else EXPECT( !!"optional: move-assignment is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-assign from/to engaged and disengaged optionals, converting, (5)" ) { SETUP( "" ) { optional d1; optional d2; optional e1( 123 ); optional e2( '7' ); SECTION( "a disengaged optional assigned an engaged optional obtains its value, converting" ) { d1 = e2; EXPECT( d1 ); EXPECT( *d1 == '7' ); } SECTION( "an engaged optional assigned an engaged optional obtains its value, converting" ) { e1 = e2; EXPECT( e1 ); EXPECT( *e1 == '7' ); } SECTION( "an engaged optional assigned a disengaged optional becomes empty, converting" ) { e1 = d2; EXPECT( !e1 ); } SECTION( "a disengaged optional assigned a disengaged optional remains empty, converting" ) { d1 = d2; EXPECT( !d1 ); }} } CASE( "optional: Allows to move-assign from/to engaged and disengaged optionals, converting (C++11, 6)" ) { #if optional_CPP11_OR_GREATER SETUP( "" ) { optional d1; optional d2; optional e1( 123 ); optional e2( '7' ); SECTION( "a disengaged optional assigned an engaged optional obtains its value, converting" ) { d1 = std::move( e2 ); EXPECT( d1 ); EXPECT( *d1 == '7' ); } SECTION( "an engaged optional assigned an engaged optional obtains its value, converting" ) { e1 = std::move( e2 ); EXPECT( e1 ); EXPECT( *e1 == '7' ); } SECTION( "an engaged optional assigned a disengaged optional becomes empty, converting" ) { e1 = std::move( d2 ); EXPECT( !e1 ); } SECTION( "a disengaged optional assigned a disengaged optional remains empty, converting" ) { d1 = std::move( d2 ); EXPECT( !d1 ); }} #else EXPECT( !!"optional: move-assignment is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-assign from literal value (4)" ) { optional a; a = 7; EXPECT( *a == 7 ); } CASE( "optional: Allows to copy-assign from value (4)" ) { const int i = 7; optional a; a = i; EXPECT( *a == i ); } CASE( "optional: Allows to move-assign from value (C++11, 4)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); optional a; a = std::move( s ); EXPECT( a->value == 7 ); EXPECT( a->state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: move-assign is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-emplace content from arguments (C++11, 7)" ) { #if optional_CPP11_OR_GREATER using pair_t = std::pair; S s( 7 ); optional a; a.emplace( 'a', s ); EXPECT( a->first == 'a' ); EXPECT( a->second.value == 7 ); EXPECT( a->second.state == copy_constructed ); EXPECT( s.state != moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to move-emplace content from arguments (C++11, 7)" ) { #if optional_CPP11_OR_GREATER using pair_t = std::pair; S s( 7 ); optional a; a.emplace( 'a', std::move( s ) ); EXPECT( a->first == 'a' ); EXPECT( a->second.value == 7 ); EXPECT( a->second.state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to copy-emplace content from intializer-list and arguments (C++11, 8)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); optional a; a.emplace( { 7, 8, 9, }, 'a', s ); EXPECT( a->vec[0] == 7 ); EXPECT( a->vec[1] == 8 ); EXPECT( a->vec[2] == 9 ); EXPECT( a->c == 'a' ); EXPECT( a->s.value == 7 ); EXPECT( a->s.state == copy_constructed ); EXPECT( s.state != moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Allows to move-emplace content from intializer-list and arguments (C++11, 8)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); optional a; a.emplace( { 7, 8, 9, }, 'a', std::move( s ) ); EXPECT( a->vec[0] == 7 ); EXPECT( a->vec[1] == 8 ); EXPECT( a->vec[2] == 9 ); EXPECT( a->c == 'a' ); EXPECT( a->s.value == 7 ); EXPECT( a->s.state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } // swap: CASE( "optional: Allows to swap with other optional (member)" ) { SETUP( "" ) { optional d1; optional d2; optional e1( 42 ); optional e2( 7 ); SECTION( "swap disengaged with disengaged optional" ) { d1.swap( d2 ); EXPECT( !d1 ); } SECTION( "swap engaged with engaged optional" ) { e1.swap( e2 ); EXPECT( e1 ); EXPECT( e2 ); EXPECT( *e1 == 7 ); EXPECT( *e2 == 42 ); } SECTION( "swap disengaged with engaged optional" ) { d1.swap( e1 ); EXPECT( d1 ); EXPECT( !e1 ); EXPECT( *d1 == 42 ); } SECTION( "swap engaged with disengaged optional" ) { e1.swap( d1 ); EXPECT( d1 ); EXPECT( !e1 ); EXPECT( *d1 == 42 ); }} } // observers: CASE( "optional: Allows to obtain value via operator->()" ) { SETUP( "" ) { optional e( Implicit( 42 ) ); optional const ce( Implicit( 42 ) ); SECTION( "operator->() yields pointer to value (const)" ) { EXPECT( ce->x == 42 ); } SECTION( "operator->() yields pointer to value (non-const)" ) { e->x = 7; EXPECT( e->x == 7 ); }} } CASE( "optional: Allows to obtain moved-value via operator->() (C++11)" ) { #if optional_CPP11_OR_GREATER SETUP( "" ) { optional e( Implicit( 42 ) ); optional const ce( Implicit( 42 ) ); SECTION( "operator->() yields pointer to value (const)" ) { EXPECT( std::move( ce )->x == 42 ); } SECTION( "operator->() yields pointer to value (non-const)" ) { e->x = 7; EXPECT( std::move( e )->x == 7 ); }} #else EXPECT( !!"optional: move-semantics are not available (no C++11)" ); #endif } CASE( "optional: Allows to obtain value via operator*()" ) { SETUP( "" ) { optional e( 42 ); optional const ce( 42 ); SECTION( "operator*() yields value (const)" ) { EXPECT( *ce == 42 ); } SECTION( "operator*() yields value (non-const)" ) { *e = 7; EXPECT( *e == 7 ); }} } CASE( "optional: Allows to obtain moved-value via operator*() (C++11)" ) { #if optional_CPP11_OR_GREATER SETUP( "" ) { optional e( 42 ); optional const ce( 42 ); SECTION( "operator*() yields value (const)" ) { EXPECT( *(std::move( ce )) == 42 ); } SECTION( "operator*() yields value (non-const)" ) { *e = 7; EXPECT( *(std::move( e )) == 7 ); }} #else EXPECT( !!"optional: move-semantics are not available (no C++11)" ); #endif } CASE( "optional: Allows to obtain has_value() via operator bool()" ) { optional a; optional b( 7 ); EXPECT_NOT( a ); EXPECT( b ); } CASE( "optional: Allows to obtain value via value()" ) { SETUP( "" ) { optional e( 42 ); optional const ce( 42 ); SECTION( "value() yields value (const)" ) { EXPECT( opt_value( ce ) == 42 ); } SECTION( "value() yields value (non-const)" ) { EXPECT( opt_value( e ) == 42 ); }} } CASE( "optional: Allows to obtain moved-value via value() (C++11)" ) { #if optional_CPP11_OR_GREATER SETUP( "" ) { optional e( 42 ); optional const ce( 42 ); SECTION( "value() yields value (const)" ) { EXPECT( opt_value( std::move( ce ) ) == 42 ); } SECTION( "value() yields value (non-const)" ) { EXPECT( opt_value( std::move( e ) ) == 42 ); }} #else EXPECT( !!"optional: move-semantics are not available (no C++11)" ); #endif } CASE( "optional: Allows to obtain value or default via value_or()" ) { SETUP( "" ) { optional d; optional e( 42 ); SECTION( "value_or( 7 ) yields value for non-empty optional" ) { EXPECT( e.value_or( 7 ) == 42 ); } SECTION( "value_or( 7 ) yields default for empty optional" ) { EXPECT( d.value_or( 7 ) == 7 ); }} } CASE( "optional: Allows to obtain moved-value or moved-default via value_or() (C++11)" ) { #if optional_CPP11_OR_GREATER SETUP( "" ) { optional d; optional e( 42 ); optional ds; optional es("77"); SECTION("for l-values") { EXPECT( d.value_or( 7 ) == 7 ); EXPECT( e.value_or( 7 ) == 42 ); EXPECT( ds.value_or("7") == "7" ); EXPECT( es.value_or("7") == "77" ); EXPECT_NOT( es->empty() ); // see issue-60 } SECTION("for r-values") { EXPECT( std::move( d ).value_or( 7 ) == 7 ); EXPECT( std::move( e ).value_or( 7 ) == 42 ); EXPECT( std::move( ds ).value_or("7") == "7" ); EXPECT( std::move( es ).value_or("7") == "77" ); }} #else EXPECT( !!"optional: move-semantics are not available (no C++11)" ); #endif } CASE( "optional: Throws bad_optional_access at disengaged access" ) { #if optional_USES_STD_OPTIONAL && defined(__APPLE__) EXPECT( true ); #else SETUP( "" ) { optional d; optional const cd; SECTION("for l-values") { EXPECT_THROWS_AS( d.value(), bad_optional_access ); EXPECT_THROWS_AS( cd.value(), bad_optional_access ); } # if optional_CPP11_OR_GREATER SECTION("for r-values") { EXPECT_THROWS_AS( std::move( d ).value(), bad_optional_access ); EXPECT_THROWS_AS( std::move( cd ).value(), bad_optional_access ); } # endif } #endif } CASE( "optional: Throws bad_optional_access with non-empty what()" ) { try { optional d; (void) d.value(); } catch( bad_optional_access const & e ) { EXPECT( ! std::string( e.what() ).empty() ); } } // modifiers: CASE( "optional: Allows to reset content" ) { optional a = 7; a.reset(); EXPECT_NOT( a.has_value() ); } // desctruction: namespace destruction { struct S { static void reset() { ctor_count() = 0; dtor_count() = 0; } static int & ctor_count() { static int i = 0; return i; } static int & dtor_count() { static int i = 0; return i; } S( int /*i*/ ) { ++ctor_count(); } S( char /*c*/, int /*i*/ ) { ++ctor_count(); } S( S const & ) { ++ctor_count(); } #if optional_CPP11_OR_GREATER S( S&& ) {} #endif ~S() { ++dtor_count(); } }; } // namespace destruct CASE( "optional: Ensure object is destructed only once (C++11)" ) { #if optional_CPP11_OR_GREATER using destruction::S; SETUP( "- Reset ctor & dtor counts" ) { S::reset(); SECTION( "- Destruction with direct initialize (C++11)" ) { { optional s( 7 ); EXPECT( s.has_value() ); EXPECT( S::dtor_count() == 0 ); } EXPECT( S::dtor_count() == 1 ); } SECTION( "- Destruction with emplace (C++11)" ) { { optional s; EXPECT( S::dtor_count() == 0 ); s.emplace( 'c', 42 ); EXPECT( S::dtor_count() == 0 ); } EXPECT( S::dtor_count() == 1 ); }} #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "optional: Ensure balanced construction-destruction (C++98)" ) { using destruction::S; SETUP( "- Reset ctor & dtor counts" ) { S::reset(); SECTION( "- Destruction with direct initialize (C++98)" ) { { optional s( 7 ); EXPECT( s.has_value() ); EXPECT( S::ctor_count() == S::dtor_count() + 1 ); } EXPECT( S::ctor_count() == S::dtor_count() ); }} } // // optional non-member functions: // CASE( "optional: Allows to swaps engage state and values (non-member)" ) { SETUP( "" ) { optional d1; optional d2; optional e1( 42 ); optional e2( 7 ); SECTION( "swap disengaged with disengaged optional" ) { swap( d1, d2 ); EXPECT( !d1 ); } SECTION( "swap engaged with engaged optional" ) { swap( e1, e2 ); EXPECT( e1 ); EXPECT( e2 ); EXPECT( *e1 == 7 ); EXPECT( *e2 == 42 ); } SECTION( "swap disengaged with engaged optional" ) { swap( d1, e1 ); EXPECT( d1 ); EXPECT( !e1 ); EXPECT( *d1 == 42 ); } SECTION( "swap engaged with disengaged optional" ) { swap( e1, d1 ); EXPECT( d1 ); EXPECT( !e1 ); EXPECT( *d1 == 42 ); }} } template< typename R, typename S, typename T > void relop( lest::env & lest_env ) { SETUP( "" ) { optional d; optional e1( 6 ); optional e2( 7 ); SECTION( "engaged == engaged" ) { EXPECT( e1 == e1 ); } SECTION( "engaged == disengaged" ) { EXPECT( !(e1 == d ) ); } SECTION( "disengaged == engaged" ) { EXPECT( !(d == e1) ); } SECTION( "engaged != engaged" ) { EXPECT( e1 != e2 ); } SECTION( "engaged != disengaged" ) { EXPECT( e1 != d ); } SECTION( "disengaged != engaged" ) { EXPECT( d != e2 ); } SECTION( "engaged < engaged" ) { EXPECT( e1 < e2 ); } SECTION( "engaged < disengaged" ) { EXPECT( !(e1 < d ) ); } SECTION( "disengaged < engaged" ) { EXPECT( d < e2 ); } SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e1 ); } SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e2 ); } SECTION( "engaged <= disengaged" ) { EXPECT( !(e1 <= d ) ); } SECTION( "disengaged <= engaged" ) { EXPECT( d <= e2 ); } SECTION( "engaged > engaged" ) { EXPECT( e2 > e1 ); } SECTION( "engaged > disengaged" ) { EXPECT( e2 > d ); } SECTION( "disengaged > engaged" ) { EXPECT( !(d > e1) ); } SECTION( "engaged >= engaged" ) { EXPECT( e1 >= e1 ); } SECTION( "engaged >= engaged" ) { EXPECT( e2 >= e1 ); } SECTION( "engaged >= disengaged" ) { EXPECT( e2 >= d ); } SECTION( "disengaged >= engaged" ) { EXPECT( !(d >= e1) ); } SECTION( "disengaged == nullopt" ) { EXPECT( (d == nullopt) ); } SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt == d ) ); } SECTION( "engaged == nullopt" ) { EXPECT( (e1 != nullopt) ); } SECTION( "nullopt == engaged" ) { EXPECT( (nullopt != e1 ) ); } SECTION( "disengaged == nullopt" ) { EXPECT( !(d < nullopt) ); } SECTION( "nullopt == disengaged" ) { EXPECT( !(nullopt < d ) ); } SECTION( "disengaged == nullopt" ) { EXPECT( (d <= nullopt) ); } SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt <= d ) ); } SECTION( "disengaged == nullopt" ) { EXPECT( !(d > nullopt) ); } SECTION( "nullopt == disengaged" ) { EXPECT( !(nullopt > d ) ); } SECTION( "disengaged == nullopt" ) { EXPECT( (d >= nullopt) ); } SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt >= d ) ); } SECTION( "engaged == value" ) { EXPECT( e1 == 6 ); } SECTION( "value == engaged" ) { EXPECT( 6 == e1 ); } SECTION( "engaged != value" ) { EXPECT( e1 != 7 ); } SECTION( "value != engaged" ) { EXPECT( 6 != e2 ); } SECTION( "engaged < value" ) { EXPECT( e1 < 7 ); } SECTION( "value < engaged" ) { EXPECT( 6 < e2 ); } SECTION( "engaged <= value" ) { EXPECT( e1 <= 7 ); } SECTION( "value <= engaged" ) { EXPECT( 6 <= e2 ); } SECTION( "engaged > value" ) { EXPECT( e2 > 6 ); } SECTION( "value > engaged" ) { EXPECT( 7 > e1 ); } SECTION( "engaged >= value" ) { EXPECT( e2 >= 6 ); } SECTION( "value >= engaged" ) { EXPECT( 7 >= e1 ); } } } CASE( "optional: Provides relational operators (non-member)" ) { relop( lest_env ); } CASE( "optional: Provides mixed-type relational operators (non-member)" ) { relop( lest_env ); } CASE( "make_optional: Allows to copy-construct optional" ) { S s( 7 ); EXPECT( make_optional( s )->value == 7 ); EXPECT( s.state != moved_from ); } CASE( "make_optional: Allows to move-construct optional (C++11)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); EXPECT( make_optional( std::move( s ) )->value == 7 ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: move-construction is not available (no C++11)" ); #endif } CASE( "make_optional: Allows to in-place copy-construct optional from arguments (C++11)" ) { #if optional_CPP11_OR_GREATER using pair_t = std::pair; S s( 7); auto a = make_optional( 'a', s ); EXPECT( a->first == 'a' ); EXPECT( a->second.value == 7 ); #if optional_USES_STD_OPTIONAL EXPECT( a->second.state == copy_constructed ); #else EXPECT( a->second.state == move_constructed ); #endif EXPECT( s.state != moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "make_optional: Allows to in-place move-construct optional from arguments (C++11)" ) { #if optional_CPP11_OR_GREATER using pair_t = std::pair; S s( 7 ); auto a = make_optional( 'a', std::move( s ) ); EXPECT( a->first == 'a' ); EXPECT( a->second.value == 7 ); EXPECT( a->second.state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "make_optional: Allows to in-place copy-construct optional from initializer-list and arguments (C++11)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); auto a = make_optional( { 7, 8, 9, }, 'a', s ); EXPECT( a->vec[0] == 7 ); EXPECT( a->vec[1] == 8 ); EXPECT( a->vec[2] == 9 ); EXPECT( a->c == 'a' ); EXPECT( a->s.value == 7 ); #if optional_USES_STD_OPTIONAL EXPECT( a->s.state == copy_constructed ); #else EXPECT( a->s.state == move_constructed ); #endif EXPECT( s.state != moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "make_optional: Allows to in-place move-construct optional from initializer-list and arguments (C++11)" ) { #if optional_CPP11_OR_GREATER S s( 7 ); auto a = make_optional( { 7, 8, 9, }, 'a', std::move( s ) ); EXPECT( a->vec[0] == 7 ); EXPECT( a->vec[1] == 8 ); EXPECT( a->vec[2] == 9 ); EXPECT( a->c == 'a' ); EXPECT( a->s.value == 7 ); EXPECT( a->s.state == move_constructed ); EXPECT( s.state == moved_from ); #else EXPECT( !!"optional: in-place construction is not available (no C++11)" ); #endif } CASE( "std::hash<>: Allows to obtain hash (C++11)" ) { #if optional_CPP11_OR_GREATER const auto a = optional( 7 ); const auto b = optional( 7 ); EXPECT( std::hash>{}( a ) == std::hash>{}( b ) ); #else EXPECT( !!"std::hash<>: std::hash<> is not available (no C++11)" ); #endif } CASE( "tweak header: reads tweak header if supported " "[tweak]" ) { #if optional_HAVE_TWEAK_HEADER EXPECT( OPTIONAL_TWEAK_VALUE == 42 ); #else EXPECT( !!"Tweak header is not available (optional_HAVE_TWEAK_HEADER: 0)." ); #endif } // // Negative tests: // // // Tests that print information: // struct Struct{ Struct(){} }; #if !defined(optional_FEATURE_MAX_ALIGN_HACK) || !optional_FEATURE_MAX_ALIGN_HACK #define optional_OUTPUT_ALIGNMENT_OF( type ) \ "alignment_of<" #type ">: " << \ alignment_of::value << "\n" << CASE("alignment_of: Show alignment of various types" "[.]" ) { #if optional_CPP11_OR_GREATER using std::alignment_of; #else using ::nonstd::optional_lite::detail::alignment_of; #endif std::cout << optional_OUTPUT_ALIGNMENT_OF( char ) optional_OUTPUT_ALIGNMENT_OF( short ) optional_OUTPUT_ALIGNMENT_OF( int ) optional_OUTPUT_ALIGNMENT_OF( long ) optional_OUTPUT_ALIGNMENT_OF( float ) optional_OUTPUT_ALIGNMENT_OF( double ) optional_OUTPUT_ALIGNMENT_OF( long double ) optional_OUTPUT_ALIGNMENT_OF( Struct ) ""; } #undef optional_OUTPUT_ALIGNMENT_OF #endif #define optional_OUTPUT_SIZEOF( type ) \ "sizeof( optional<" #type "> ): " << \ sizeof( optional< type> ) << " (" << sizeof(type) << ")\n" << CASE("storage_t: Show sizeof various optionals" "[.]" ) { std::cout << #if !optional_USES_STD_OPTIONAL "sizeof( nonstd::optional_lite::detail::storage_t ): " << sizeof( nonstd::optional_lite::detail::storage_t ) << "\n" << #endif optional_OUTPUT_SIZEOF( char ) optional_OUTPUT_SIZEOF( short ) optional_OUTPUT_SIZEOF( int ) optional_OUTPUT_SIZEOF( long ) optional_OUTPUT_SIZEOF( float ) optional_OUTPUT_SIZEOF( double ) optional_OUTPUT_SIZEOF( long double ) optional_OUTPUT_SIZEOF( Struct ) ""; } #undef optional_OUTPUT_SIZEOF // // Issues: // CASE( "optional: isocpp-lib: CH 3, p0032r2 -- let's not have too clever tags" "[.issue-1]" ) { EXPECT( false ); #if 0 optional< optional< optional > > a ( in_place, #if 0 in_place, #else // nonstd_lite_in_place_type_t(int), static_cast< nonstd::in_place_t >( in_place ), #endif nullopt ); EXPECT( a ); EXPECT( *a ); EXPECT_NOT( **a ); #endif } // end of file