2275 lines
62 KiB
C++
2275 lines
62 KiB
C++
// 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<int> vec;
|
|
char c;
|
|
|
|
InitList( std::initializer_list<int> 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<T,E> 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<E> 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<std::exception_ptr>
|
|
|
|
CASE( "unexpected_type: Disallows default construction" )
|
|
{
|
|
#if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS
|
|
unexpected_type<int> u;
|
|
#endif
|
|
}
|
|
|
|
CASE( "unexpected_type: Allows to copy-construct from unexpected_type, default" )
|
|
{
|
|
unexpected_type<int> a{ 7 };
|
|
|
|
unexpected_type<int> b( a );
|
|
|
|
EXPECT( a.error() == 7 );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "unexpected_type: Allows to move-construct from unexpected_type, default" )
|
|
{
|
|
unexpected_type<int> a{ 7 };
|
|
|
|
unexpected_type<int> b( std::move( a ) );
|
|
|
|
EXPECT( a.error() == 7 );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "unexpected_type: Allows to in-place-construct" )
|
|
{
|
|
unexpected_type<Explicit> ue( in_place, 5 );
|
|
unexpected_type<Implicit> 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<InitList> 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<int> u{ 7 };
|
|
|
|
EXPECT( u.error() == 7 );
|
|
}
|
|
|
|
CASE( "unexpected_type: Allows to move-construct from error_type" )
|
|
{
|
|
unexpected_type<int> 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<int> a{ 7 };
|
|
|
|
unexpected_type<Explicit> 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<int> a{ 7 };
|
|
|
|
unexpected_type<Implicit> 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<int> a{ 7 };
|
|
|
|
unexpected_type<Explicit> 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<int> a{ 7 };
|
|
|
|
unexpected_type<Implicit> 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<int> a{ 7 };
|
|
unexpected_type<int> b{ 0 };
|
|
|
|
b = a;
|
|
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "unexpected_type: Allows to move-assign from unexpected_type, default" )
|
|
{
|
|
unexpected_type<int> a{ 7 };
|
|
unexpected_type<int> 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<int> u{ 7 };
|
|
unexpected_type<Explicit> ue{ 0 };
|
|
unexpected_type<Implicit> 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<int> u{ 7 };
|
|
unexpected_type<int> v{ 7 };
|
|
unexpected_type<Explicit> ue{ 0 };
|
|
unexpected_type<Implicit> 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<int> u{ 7 };
|
|
unexpected_type<int> 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<int> u{ 7 };
|
|
unexpected_type<int> 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<int> 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<int> u{ 7 };
|
|
//
|
|
// std::move( u.error() ) = v;
|
|
//
|
|
// EXPECT( u.error() == v );
|
|
//}
|
|
|
|
CASE( "unexpected_type: Allows to be swapped" )
|
|
{
|
|
unexpected_type<int> a{ 5 };
|
|
unexpected_type<int> b{ 7 };
|
|
|
|
a.swap( b );
|
|
|
|
EXPECT( a.error() == 7 );
|
|
EXPECT( b.error() == 5 );
|
|
}
|
|
|
|
//CASE( "unexpected_type: Allows reset via = {}" )
|
|
//{
|
|
// unexpected_type<int> u( 3 );
|
|
//
|
|
// u = {};
|
|
//}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// unexpected_type<std::exception_ptr>
|
|
|
|
// 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<std::exception_ptr>: Disallows default construction" )
|
|
{
|
|
#if nsel_CONFIG_CONFIRMS_COMPILATION_ERRORS
|
|
unexpected_type<std::exception_ptr> u;
|
|
#endif
|
|
}
|
|
|
|
CASE( "unexpected_type<std::exception_ptr>: Allows to copy-construct from error_type" )
|
|
{
|
|
auto ep = make_ep();
|
|
|
|
unexpected_type<std::exception_ptr> u{ ep };
|
|
|
|
EXPECT( u.error() == ep );
|
|
}
|
|
|
|
CASE( "unexpected_type<std::exception_ptr>: Allows to move-construct from error_type" )
|
|
{
|
|
auto ep_move = make_ep();
|
|
const auto ep_copy = ep_move;
|
|
|
|
unexpected_type<std::exception_ptr> u{ std::move( ep_move ) };
|
|
|
|
EXPECT( u.error() == ep_copy );
|
|
}
|
|
|
|
CASE( "unexpected_type<std::exception_ptr>: Allows to copy-construct from an exception" )
|
|
{
|
|
std::string text = "hello, world";
|
|
|
|
unexpected_type<std::exception_ptr> 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<std::exception_ptr>: Allows to observe its value" )
|
|
{
|
|
const auto ep = make_ep();
|
|
unexpected_type<std::exception_ptr> u{ ep };
|
|
|
|
EXPECT( u.error() == ep );
|
|
}
|
|
|
|
CASE( "unexpected_type<std::exception_ptr>: Allows to modify its value" )
|
|
{
|
|
const auto ep1 = make_ep();
|
|
const auto ep2 = make_ep();
|
|
unexpected_type<std::exception_ptr> u{ ep1 };
|
|
|
|
u.error() = ep2;
|
|
|
|
EXPECT( u.error() == ep2 );
|
|
}
|
|
|
|
//CASE( "unexpected_type: Allows reset via = {}, std::exception_ptr specialization" )
|
|
//{
|
|
// unexpected_type<int> u( 3 );
|
|
//
|
|
// u = {};
|
|
//}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// unexpected_type relational operators
|
|
|
|
CASE( "unexpected_type: Provides relational operators" )
|
|
{
|
|
SETUP( "" ) {
|
|
unexpected_type<int> u1( 6 );
|
|
unexpected_type<int> 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<std::exception_ptr> u( make_ep() );
|
|
unexpected_type<std::exception_ptr> 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<X>: Is true for unexpected_type" "[.deprecated]" )
|
|
{
|
|
#if nsel_P0323R <= 3
|
|
EXPECT( is_unexpected<unexpected_type<std::exception_ptr>>::value );
|
|
#else
|
|
EXPECT( !!"is_unexpected<> is not available (nsel_P0323R > 3)" );
|
|
#endif
|
|
}
|
|
|
|
CASE( "is_unexpected<X>: Is false for non-unexpected_type (int)" "[.deprecated]" )
|
|
{
|
|
#if nsel_P0323R <= 3
|
|
EXPECT_NOT( is_unexpected<int>::value );
|
|
#else
|
|
EXPECT( !!"is_unexpected<> is not available (nsel_P0323R > 3)" );
|
|
#endif
|
|
}
|
|
|
|
// unexpected: factory
|
|
|
|
CASE( "make_unexpected(): Allows to create an unexpected_type<E> 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<E> from an E" )
|
|
{
|
|
const auto a = 'a';
|
|
const auto b = 7;
|
|
|
|
auto u = make_unexpected< std::pair<char, int> >( 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<std::exception_ptr> 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<int> 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<int> bad;
|
|
#endif
|
|
}
|
|
|
|
CASE( "bad_expected_access: Allows construction from error_type" )
|
|
{
|
|
bad_expected_access<int> bea( 123 );
|
|
}
|
|
|
|
CASE( "bad_expected_access: Allows to observe its error" )
|
|
{
|
|
const int error = 7;
|
|
bad_expected_access<int> bea( error );
|
|
bad_expected_access<int> 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<int> bea( old_error );
|
|
|
|
bea.error() = new_error;
|
|
|
|
EXPECT( bea.error() == new_error );
|
|
}
|
|
|
|
CASE( "bad_expected_access: Provides non-empty what()" )
|
|
{
|
|
bad_expected_access<int> bea( 123 );
|
|
|
|
EXPECT( ! std::string( bea.what() ).empty() );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// expected<>
|
|
|
|
// x.x.4.1 expected<> constructors (value)
|
|
|
|
CASE( "expected: Allows to default construct" )
|
|
{
|
|
expected<int, char> e;
|
|
|
|
EXPECT( e.has_value() );
|
|
}
|
|
|
|
CASE( "expected: Allows to default construct from noncopyable, noncopyable value type" )
|
|
{
|
|
expected<NonMovableNonCopyable, NonMovableNonCopyable> e;
|
|
|
|
EXPECT( e.has_value() );
|
|
}
|
|
|
|
CASE( "expected: Allows to default construct from noncopyable, noncopyable error type" )
|
|
{
|
|
expected<NonMovableNonCopyable, NonMovableNonCopyable> e{ unexpect_t{} };
|
|
|
|
EXPECT( !e.has_value() );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-construct from expected: value" )
|
|
{
|
|
expected<int, char> a = 7;
|
|
|
|
expected<int, char> b{ a };
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-construct from expected: error" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
|
|
expected<char, int> b{ a };
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from expected: value" )
|
|
{
|
|
expected<int, char> a = 7;
|
|
|
|
expected<int, char> b{ std::move( a ) };
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == 7 );
|
|
EXPECT( a ); // postcondition: unchanged!
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from expected: error" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
|
|
expected<char, int> b{ std::move( a ) };
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-construct from expected; value, explicit converting" )
|
|
{
|
|
expected<int, char> a = 7;
|
|
|
|
expected<Explicit, char> b{ a };
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-construct from expected; error, explicit converting" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
|
|
expected<char, Explicit> b{ a };
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-construct from expected; value, non-explicit converting" )
|
|
{
|
|
expected<int, char> a = 7;
|
|
|
|
expected<Implicit, char> b( a );
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-construct from expected; error, non-explicit converting" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
|
|
expected<char, Implicit> b( a );
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from expected; value, explicit converting" )
|
|
{
|
|
expected<int, char> a = 7;
|
|
|
|
expected<Explicit, char> b{ std::move( a ) };
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from expected; error, explicit converting" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
|
|
expected<char, Explicit> b{ std::move( a ) };
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from expected; value, non-explicit converting" )
|
|
{
|
|
expected<int, char> a = 7;
|
|
|
|
expected<Implicit, char> b( std::move( a ) );
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from expected; error, non-explicit converting" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
|
|
expected<char, Implicit> 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<Explicit, char> 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<Implicit, char> b{ std::move( v ) };
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to in-place-construct value" )
|
|
{
|
|
auto v = 7;
|
|
|
|
expected<Implicit, char> b{ in_place, v };
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to in-place-construct value from initializer_list" )
|
|
{
|
|
expected<InitList, char> 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<int> u{ 7 };
|
|
|
|
expected<int, Explicit> e{ u };
|
|
|
|
EXPECT( e.error() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-construct from unexpected, non-explicit converting" )
|
|
{
|
|
unexpected_type<int> u{ 7 };
|
|
|
|
expected<int, Implicit> e{ u };
|
|
|
|
EXPECT( e.error() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from unexpected, explicit converting" )
|
|
{
|
|
unexpected_type<int> u{ 7 };
|
|
|
|
expected<int, Explicit> e{ std::move( u ) };
|
|
|
|
EXPECT( e.error() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-construct from unexpected, non-explicit converting" )
|
|
{
|
|
unexpected_type<int> u{ 7 };
|
|
|
|
expected<int, Implicit> e{ std::move( u ) };
|
|
|
|
EXPECT( e.error() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected: Allows to in-place-construct error" )
|
|
{
|
|
expected<int, Explicit> e{ unexpect, 7 };
|
|
expected<int, Implicit> 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<char, InitList> 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<int, char> a{ 7 };
|
|
expected<int, char> b;
|
|
|
|
b = a;
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to copy-assign from expected, error" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
expected<char, int> b;
|
|
|
|
b = a;
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-assign from expected, value" )
|
|
{
|
|
expected<int, char> a{ 7 };
|
|
expected<int, char> b;
|
|
|
|
b = std::move( a );
|
|
|
|
EXPECT( b );
|
|
EXPECT( b.value() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-assign from expected, error" )
|
|
{
|
|
expected<char, int> a{ unexpect, 7 };
|
|
expected<char, int> b;
|
|
|
|
b = std::move( a );
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to forward-assign from value" )
|
|
{
|
|
expected<int, char> a;
|
|
expected<int, char> 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<char, int> e;
|
|
unexpected_type<int> u{ 7 } ;
|
|
|
|
e = u;
|
|
|
|
EXPECT( e.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-assign from unexpected" )
|
|
{
|
|
expected<char, int> e;
|
|
unexpected_type<int> u{ 7 } ;
|
|
|
|
e = std::move( u );
|
|
|
|
EXPECT( e.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to move-assign from move-only unexpected" )
|
|
{
|
|
expected<char, MoveOnly> e;
|
|
unexpected_type<MoveOnly> u{7};
|
|
|
|
e = std::move( u );
|
|
|
|
EXPECT( e.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected: Allows to emplace value" )
|
|
{
|
|
expected<int, char> a;
|
|
expected<int, char> 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<InitList, char> e{ in_place, std::initializer_list<int>{}, '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<int, char> e1{ 1 };
|
|
expected<int, char> e2{ 2 };
|
|
expected<int, char> u1{ unexpect, '1' };
|
|
expected<int, char> 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<Implicit, char> e{ v };
|
|
|
|
EXPECT( e->x == v );
|
|
}
|
|
|
|
CASE( "expected: Allows to observe its value via a pointer to constant" )
|
|
{
|
|
const auto v = 7;
|
|
const expected<Implicit, char> 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<Implicit, char> 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<Implicit, char> e{ v };
|
|
expected<Implicit, char> 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<Implicit, char> e{ v };
|
|
expected<Implicit, char> 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<Implicit, char> 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<Implicit, char> 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<int, int> e;
|
|
expected<int, int> u{ unexpect, 3 };
|
|
|
|
EXPECT( e );
|
|
EXPECT( !u );
|
|
}
|
|
|
|
CASE( "expected: Allows to observe its value" )
|
|
{
|
|
const auto v = 7;
|
|
expected<Implicit, char> e{ v };
|
|
expected<Implicit, char> 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<Implicit, char> e{ v1 };
|
|
|
|
e.value() = Implicit{v2};
|
|
|
|
EXPECT( e.value() == Implicit{v2} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move its value" )
|
|
{
|
|
const auto v = 7;
|
|
expected<Implicit, char> m{ v };
|
|
expected<Implicit, char> const mc{ v };
|
|
|
|
expected<Implicit, char> e{ std::move(m ).value() };
|
|
expected<Implicit, char> 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<char, Implicit> e{ unexpect, v };
|
|
expected<char, Implicit> 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<char, Implicit> e{ unexpect, v1 };
|
|
|
|
e.error() = Implicit{v2};
|
|
|
|
EXPECT( e.error() == Implicit{v2} );
|
|
}
|
|
|
|
CASE( "expected: Allows to move its error" )
|
|
{
|
|
const auto v = 7;
|
|
expected<char, Implicit> m{ unexpect, v };
|
|
expected<char, Implicit> const mc{ unexpect, v };
|
|
|
|
expected<char, Implicit> e{ unexpect, std::move(m ).error() };
|
|
expected<char, Implicit> 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<char, Implicit> 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<int, std::out_of_range> 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<int, int> e{ ve };
|
|
expected<int, int> 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<int, int> mv{ ve };
|
|
expected<int, int> 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<char, int > e{ unexpect, 7 };
|
|
expected<char, int > ec{ unexpect, 7 };
|
|
|
|
EXPECT_THROWS( e.value() );
|
|
EXPECT_THROWS_AS( e.value(), bad_expected_access<int> );
|
|
EXPECT_THROWS( ec.value() );
|
|
EXPECT_THROWS_AS( ec.value(), bad_expected_access<int> );
|
|
|
|
EXPECT_THROWS( std::move( e).value() );
|
|
EXPECT_THROWS_AS( std::move( e).value(), bad_expected_access<int> );
|
|
EXPECT_THROWS( std::move(ec).value() );
|
|
EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access<int> );
|
|
}
|
|
|
|
#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<int, int> e{ ve };
|
|
expected<int, int> 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<int, int> { return n * 2; };
|
|
const auto to_unexpect42 = []( int ) -> expected<int, int> { return make_unexpected( 42 ); };
|
|
|
|
{
|
|
expected<int, int> e{ 11 };
|
|
const expected<int, int> ce{ 21 };
|
|
expected<int, int> 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<int, int> { return val.x * 2; };
|
|
EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).and_then( moveonly_x_mul2 ).value() == 66 );
|
|
EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 15 } }).and_then( [](MoveOnly&&) -> expected<MoveOnly, int> { return make_unexpected( 42 ); } ).error() == 42 );
|
|
|
|
const auto map_to_void = [](int) -> expected<void, int> { return {}; };
|
|
const auto map_to_void_unexpect42 = [](int) -> expected<void, int> { return make_unexpected( 42 ); };
|
|
static_assert( std::is_same< expected<void, int>, decltype( expected<int, int>( 3 ).and_then( map_to_void ) ) >::value,
|
|
"and_then mapping to void results in expected<void>");
|
|
EXPECT( (expected<int, int>(3)).and_then( map_to_void ).has_value() );
|
|
EXPECT( !(expected<int, int>(3)).and_then( map_to_void_unexpect42 ).has_value() );
|
|
EXPECT( (expected<int, int>(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<int, int> { return make_unexpected( 43 ); };
|
|
|
|
{
|
|
expected<int, int> e{ 11 };
|
|
const expected<int, int> ce{ 21 };
|
|
expected<int, int> 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<int, int> { throw std::runtime_error( "or_else" ); };
|
|
EXPECT_THROWS_AS( (expected<int, int>{ unexpect, 42 }).or_else( fallback_throw ), std::runtime_error );
|
|
|
|
const auto moveonly_fallback_to_66 = [](int) -> expected<MoveOnly, int> { return MoveOnly{ 66 }; };
|
|
EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).or_else( moveonly_fallback_to_66 ).value() == 33 );
|
|
EXPECT( (expected<MoveOnly, int>{ 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<int, int> e{ 11 };
|
|
const expected<int, int> ce{ 21 };
|
|
expected<int, int> 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, int>{ MoveOnly{ 33 } }).transform( moveonly_map_to_x ).value() == 33 );
|
|
EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).transform( moveonly_map_to_x ).transform( mul2 ).value() == 66 );
|
|
EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).transform( moveonly_x_mul2 ).has_value() );
|
|
EXPECT( (expected<MoveOnly, int>{ MoveOnly{ 33 } }).transform( moveonly_x_mul2 ).value() == 66 );
|
|
EXPECT( !(expected<MoveOnly, int>{ unexpect, 15 }).transform( [](MoveOnly&&) -> int { return 42; } ).has_value() );
|
|
EXPECT( (expected<MoveOnly, int>{ unexpect, 15 }).transform( [](MoveOnly&&) -> int { return 42; } ).error() == 15 );
|
|
|
|
const auto map_to_void = [](int) -> void { };
|
|
static_assert( std::is_same< expected<void, int>, decltype( expected<int, int>( 3 ).transform( map_to_void ) ) >::value,
|
|
"transform to void results in expected<void>" );
|
|
EXPECT( (expected<int, int>(3)).transform( map_to_void ).has_value() );
|
|
static_assert( std::is_same< decltype( (expected<int, int>(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<int, int> e{ 11 };
|
|
const expected<int, int> ce{ 21 };
|
|
expected<int, int> 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<void> specialization
|
|
|
|
// x.x.4.1 expected<void> constructors
|
|
|
|
CASE( "expected<void>: Allows to default-construct" )
|
|
{
|
|
expected<void, char> e;
|
|
|
|
EXPECT( e.has_value() );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to copy-construct from expected<void>: value" )
|
|
{
|
|
expected<void, int> a;
|
|
|
|
expected<void, int> b{ a };
|
|
|
|
EXPECT( b );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to copy-construct from expected<void>: error" )
|
|
{
|
|
expected<void, int > a{ unexpect, 7 };
|
|
|
|
expected<void, int> b{ a };
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to move-construct from expected<void>: value" )
|
|
{
|
|
expected<void, int> a;
|
|
|
|
expected<void, int> b{ std::move( a ) };
|
|
|
|
EXPECT( b );
|
|
EXPECT( a ); // postcondition: unchanged!
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to move-construct from expected<void>: error" )
|
|
{
|
|
expected<void, int > a{ unexpect, 7 };
|
|
|
|
expected<void, int> b{ std::move( a ) };
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to in-place-construct" )
|
|
{
|
|
expected<void, int > e{ in_place };
|
|
|
|
EXPECT( e );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to copy-construct from unexpected, explicit converting" )
|
|
{
|
|
unexpected_type<int> u{ 7 };
|
|
|
|
expected<void, Explicit> e{ u };
|
|
|
|
EXPECT( e.error() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to copy-construct from unexpected, non-explicit converting" )
|
|
{
|
|
unexpected_type<int> u{ 7 };
|
|
|
|
expected<void, Implicit> e{ u };
|
|
|
|
EXPECT( e.error() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to move-construct from unexpected, explicit converting" )
|
|
{
|
|
unexpected_type<int> u{ 7 };
|
|
|
|
expected<void, Explicit> e{ std::move( u ) };
|
|
|
|
EXPECT( e.error() == Explicit{7} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to move-construct from unexpected, non-explicit converting" )
|
|
{
|
|
unexpected_type<int> u{ 7 };
|
|
|
|
expected<void, Implicit> e{ std::move( u ) };
|
|
|
|
EXPECT( e.error() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to in-place-construct unexpected_type" )
|
|
{
|
|
expected<void, Explicit> e{ unexpect, 7 };
|
|
expected<void, Implicit> i{ unexpect, 7 };
|
|
|
|
EXPECT( e.error() == Explicit{7} );
|
|
EXPECT( i.error() == Implicit{7} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to in-place-construct error from initializer_list" )
|
|
{
|
|
expected<void, InitList> 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<void> assignment
|
|
|
|
CASE( "expected<void>: Allows to copy-assign from expected, value" )
|
|
{
|
|
expected<void, int> a;
|
|
expected<void, int> b;
|
|
|
|
b = a;
|
|
|
|
EXPECT( b );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to copy-assign from expected, error" )
|
|
{
|
|
expected<void, int> a{ unexpect, 7 };
|
|
expected<void, int> b;
|
|
|
|
b = a;
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to move-assign from expected, value" )
|
|
{
|
|
expected<void, int> a;
|
|
expected<void, int> b;
|
|
|
|
b = std::move( a );
|
|
|
|
EXPECT( b );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to move-assign from expected, error" )
|
|
{
|
|
expected<void, int> a{ unexpect, 7 };
|
|
expected<void, int> b;
|
|
|
|
b = std::move( a );
|
|
|
|
EXPECT( !b );
|
|
EXPECT( b.error() == 7 );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to emplace value" )
|
|
{
|
|
expected<void, int> a{ unexpect, 7 };
|
|
|
|
a.emplace();
|
|
|
|
EXPECT( a );
|
|
}
|
|
|
|
// x.x.4.4 expected<void> swap
|
|
|
|
CASE( "expected<void>: Allows to be swapped" )
|
|
{
|
|
using std::swap;
|
|
|
|
SETUP("") {
|
|
|
|
expected<void, char> e1;
|
|
expected<void, char> e2;
|
|
expected<void, char> u1{ unexpect, '1' };
|
|
expected<void, char> 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<void> observers
|
|
|
|
CASE( "expected<void>: Allows to observe if it contains a value (or error)" )
|
|
{
|
|
expected<void, int> e;
|
|
expected<void, int> u{ unexpect, 3 };
|
|
|
|
EXPECT( e );
|
|
EXPECT( !u );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to observe its value" )
|
|
{
|
|
expected<void, int> e;
|
|
expected<void, int> const ec;
|
|
|
|
EXPECT( ( e.value(), true ) );
|
|
EXPECT( ( ec.value(), true ) );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to observe its error" )
|
|
{
|
|
const auto v = 7;
|
|
expected<void, Implicit> e{ unexpect, v };
|
|
expected<void, Implicit> const ec{ unexpect, v };
|
|
|
|
EXPECT( e.error() == Implicit{v} );
|
|
EXPECT( ec.error() == Implicit{v} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to modify its error" )
|
|
{
|
|
const auto v1 = 7;
|
|
const auto v2 = 42;
|
|
expected<void, Implicit> e{ unexpect, v1 };
|
|
|
|
e.error() = Implicit{v2};
|
|
|
|
EXPECT( e.error() == Implicit{v2} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to move its error" )
|
|
{
|
|
const auto v = 7;
|
|
expected<void, Implicit> m{ unexpect, v };
|
|
expected<void, Implicit> const mc{ unexpect, v };
|
|
|
|
expected<void, Implicit> e{ unexpect, std::move(m ).error() };
|
|
expected<void, Implicit> ec{ unexpect, std::move(mc).error() };
|
|
|
|
EXPECT( e.error() == Implicit{v} );
|
|
EXPECT( ec.error() == Implicit{v} );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to observe its error as unexpected" )
|
|
{
|
|
#if !nsel_USES_STD_EXPECTED
|
|
const auto value = 7;
|
|
expected<void, int> 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<void>: Allows to query if it contains an exception of a specific base type" )
|
|
{
|
|
#if !nsel_USES_STD_EXPECTED
|
|
expected<void, std::out_of_range> 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<void>: Throws bad_expected_access on value access when disengaged" )
|
|
{
|
|
expected<void, int > e{ unexpect, 7 };
|
|
expected<void, int > ec{ unexpect, 7 };
|
|
|
|
EXPECT_THROWS( e.value() );
|
|
EXPECT_THROWS_AS( e.value(), bad_expected_access<int> );
|
|
EXPECT_THROWS( ec.value() );
|
|
EXPECT_THROWS_AS( ec.value(), bad_expected_access<int> );
|
|
|
|
EXPECT_THROWS( std::move( e).value() );
|
|
EXPECT_THROWS_AS( std::move( e).value(), bad_expected_access<int> );
|
|
EXPECT_THROWS( std::move(ec).value() );
|
|
EXPECT_THROWS_AS( std::move(ec).value(), bad_expected_access<int> );
|
|
}
|
|
|
|
#if nsel_P2505R >= 4
|
|
CASE( "expected<void>: Allows to observe unexpected value, or fallback to a default value with error_or" " [monadic p2505r4]" )
|
|
{
|
|
const auto vu = 7;
|
|
expected<void, int> e;
|
|
expected<void, int> 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<void>: Allows to call argless functions with and_then" " [monadic p2505r3]" )
|
|
{
|
|
const auto ret22 = []() -> expected<int, int> { return 22; };
|
|
const auto unexpect32 = []() -> expected<int, int> { return make_unexpected( 32 ); };
|
|
|
|
{
|
|
expected<void, int> e;
|
|
const expected<void, int> ce;
|
|
expected<void, int> 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<void, int> e;
|
|
e.and_then( [&called]() -> expected<void, int> {
|
|
called = true;
|
|
return {};
|
|
} );
|
|
EXPECT( called );
|
|
}
|
|
|
|
{
|
|
bool called = false;
|
|
expected<void, int>{}.and_then( [&called]() -> expected<void, int> {
|
|
called = true;
|
|
return {};
|
|
} );
|
|
EXPECT( called );
|
|
}
|
|
|
|
{
|
|
bool called = false;
|
|
expected<void, int>{ unexpect, 42 }.and_then( [&called]() -> expected<void, int> {
|
|
called = true;
|
|
return {};
|
|
} );
|
|
EXPECT( !called );
|
|
}
|
|
|
|
const bool map_to_unexpect_success = !expected<void, int>{}.and_then( []() -> expected<void, int> { return make_unexpected( 42 ); } ).has_value();
|
|
EXPECT( map_to_unexpect_success );
|
|
}
|
|
|
|
CASE( "expected<void>: Allows to map to expected or unexpected with or_else" " [monadic p2505r3]" )
|
|
{
|
|
const auto make_valid = [](int) -> expected<void, int> { return {}; };
|
|
const auto unexpect32 = [](int) -> expected<void, int> { return make_unexpected( 32 ); };
|
|
|
|
{
|
|
expected<void, int> e;
|
|
const expected<void, int> ce;
|
|
expected<void, int> 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<void>: 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<void, int> 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<void>: 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<void, int> e;
|
|
const expected<void, int> ce;
|
|
expected<void, int> 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<int, char> e1( v1 );
|
|
expected<int, char> e2( v2 );
|
|
|
|
unexpected_type<char> u( 'u' );
|
|
expected<int, char> 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<void, char> ev1;
|
|
expected<void, char> ev2;
|
|
expected<void, char> 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<int, char> e1{ 1 };
|
|
expected<int, char> e2{ 2 };
|
|
expected<int, char> u1{ unexpect, '1' };
|
|
expected<int, char> 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<int, char> a{ 7 };
|
|
expected<int, char> b{ 7 };
|
|
|
|
EXPECT( (std::hash< expected<int, char> >{}( a )) == (std::hash< expected<int, char> >{}( b )) );
|
|
#else
|
|
EXPECT( !!"std::hash<std::expected<>> is not available for std::unexpected (C++23)." );
|
|
#endif
|
|
}
|
|
|
|
#if nsel_P0323R <= 3
|
|
|
|
#include <memory>
|
|
|
|
void vfoo() {}
|
|
|
|
expected<int> foo()
|
|
{
|
|
return make_expected( 7 );
|
|
}
|
|
|
|
expected<std::unique_ptr<int>> bar()
|
|
{
|
|
return make_expected( std::unique_ptr<int>( 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<void>" "[.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<int> ei = foo();
|
|
expected<std::unique_ptr<int>> 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<int, int> a{7};
|
|
const expected<int, int> ca{7};
|
|
expected<int, int> b{unexpect, 7};
|
|
const expected<int, int> cb{unexpect, 7};
|
|
expected<void, int> c{unexpect, 7};
|
|
const expected<void, int> 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<MyConstMemberNonMoveableObject> create_nonmoveable()
|
|
{
|
|
return nonstd::make_unexpected<MyConstMemberNonMoveableObject>( MyConstMemberNonMoveableObject(3) );
|
|
}
|
|
|
|
} // namespace issue_50
|
|
|
|
namespace issue_51 {
|
|
|
|
int compare_equal_with_expected_void()
|
|
{
|
|
auto ev1 = nonstd::expected<void, int>{};
|
|
auto ev2 = nonstd::expected<void, int>{};
|
|
return ev1 == ev2;
|
|
}
|
|
|
|
int compare_not_equal_with_expected_void()
|
|
{
|
|
auto ev1 = nonstd::expected<void, int>{};
|
|
auto ev2 = nonstd::expected<void, int>{};
|
|
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<issue_59::NonMovableNonCopyable>::value, "is not copy constructible" );
|
|
static_assert( !std::is_move_constructible<issue_59::NonMovableNonCopyable>::value, "is not move constructible" );
|
|
|
|
nonstd::expected<issue_59::NonMovableNonCopyable, issue_59::NonMovableNonCopyable> expected;
|
|
nonstd::expected<issue_59::NonMovableNonCopyable, issue_59::NonMovableNonCopyable> 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<A> ref{ mut_lval };
|
|
const std::reference_wrapper<const A> 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<T, nullopt_t>;
|
|
|
|
#endif
|