Files
boost_optional/test/optional_test.cpp
Fernando Cacciola 6277c98514 Initial versions
[SVN r16995]
2003-01-22 17:54:11 +00:00

899 lines
20 KiB
C++

// (C) 2002, Fernando Luis Cacciola Carballal.
//
// This material is provided "as is", with absolutely no warranty expressed
// or implied. Any use is at your own risk.
//
// Permission to use or copy this software for any purpose is hereby granted
// without fee, provided the above notices are retained on all copies.
// Permission to modify the code and to distribute modified code is granted,
// provided the above notices are retained, and a notice that the code was
// modified is included with the above copyright notice.
//
// You are welcome to contact the author at:
// fernando_cacciola@hotmail.com
//
#include<iostream>
#include<stdexcept>
#include<string>
#define BOOST_ENABLE_ASSERT_HANDLER
#include "boost/optional.hpp"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "boost/test/minimal.hpp"
namespace boost {
bool assertion_failed (char const * expr, char const * func, char const * file, long line)
{
throw std::logic_error( std::string("Boost Error: assertion failed at\nfile: ")
+ std::string(file)
+ std::string("\nfunction: ")
+ std::string(func)
) ;
}
}
using boost::optional ;
#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
using boost::swap ;
using boost::get_pointer ;
#endif
#define TRACE_LIFETIME(msg) if ( trace_lifetime ) { std::cout << msg << std::endl ; }
#define ARG(T) (static_cast< T const* >(0))
//#define SHOW_COMPILATION_FAIL_1
//#define SHOW_COMPILATION_FAIL_2
//#define SHOW_COMPILATION_FAIL_3
//#define SHOW_COMPILATION_FAIL_4a
//#define SHOW_COMPILATION_FAIL_4b
//#define SHOW_COMPILATION_FAIL_5a
//#define SHOW_COMPILATION_FAIL_5b
//
// Helper class used to verify the lifetime managment of the values held by optional
//
class X
{
public :
X ( int av ) : v(av)
{
++ count ;
TRACE_LIFETIME ( "X::X(" << av << "). this=" << this ) ;
}
X ( X const& rhs ) : v(rhs.v)
{
pending_copy = false ;
TRACE_LIFETIME ( "X::X( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
if ( throw_on_copy )
{
TRACE_LIFETIME ( "throwing exception in X's copy ctor" ) ;
throw 0 ;
}
++ count ;
}
~X()
{
pending_dtor = false ;
-- count ;
TRACE_LIFETIME ( "X::~X(). v=" << v << " this=" << this );
}
X& operator= ( X const& rhs )
{
v = rhs.v ;
TRACE_LIFETIME ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
return *this ;
}
friend bool operator == ( X const& a, X const& b )
{ return a.v == b.v ; }
friend bool operator != ( X const& a, X const& b )
{ return a.v != b.v ; }
friend bool operator < ( X const& a, X const& b )
{ return a.v < b.v ; }
int V() const { return v ; }
int& V() { return v ; }
static int count ;
static bool pending_copy ;
static bool pending_dtor ;
static bool trace_lifetime ;
static bool throw_on_copy ;
private :
int v ;
private :
X() ;
} ;
int X::count = 0 ;
bool X::trace_lifetime = false ;
bool X::pending_copy = false ;
bool X::pending_dtor = false ;
bool X::throw_on_copy = false ;
inline void set_pending_copy ( X const* x ) { X::pending_copy = true ; }
inline void set_pending_dtor ( X const* x ) { X::pending_dtor = true ; }
inline void set_throw_on_copy ( X const* x ) { X::throw_on_copy = true ; }
inline void reset_throw_on_copy ( X const* x ) { X::throw_on_copy = false ; }
inline void check_is_pending_copy ( X const* x ) { BOOST_CHECK( X::pending_copy ) ; }
inline void check_is_pending_dtor ( X const* x ) { BOOST_CHECK( X::pending_dtor ) ; }
inline void check_is_not_pending_copy( X const* x ) { BOOST_CHECK( !X::pending_copy ) ; }
inline void check_is_not_pending_dtor( X const* x ) { BOOST_CHECK( !X::pending_dtor ) ; }
inline void check_instance_count ( int c, X const* x ) { BOOST_CHECK( X::count == c ) ; }
inline int get_instance_count ( X const* x ) { return X::count ; }
inline void set_pending_copy (...) {}
inline void set_pending_dtor (...) {}
inline void set_throw_on_copy (...) {}
inline void reset_throw_on_copy (...) {}
inline void check_is_pending_copy (...) {}
inline void check_is_pending_dtor (...) {}
inline void check_is_not_pending_copy(...) {}
inline void check_is_not_pending_dtor(...) {}
inline void check_instance_count (...) {}
inline int get_instance_count (...) { return 0 ; }
template<class T>
inline void check_uninitialized_const ( optional<T> const& opt )
{
BOOST_CHECK( opt == 0 ) ;
BOOST_CHECK( !opt ) ;
BOOST_CHECK( !get_pointer(opt) ) ;
BOOST_CHECK( !opt.get() ) ;
}
template<class T>
inline void check_uninitialized ( optional<T>& opt )
{
BOOST_CHECK( opt == 0 ) ;
BOOST_CHECK( !opt ) ;
BOOST_CHECK( !get_pointer(opt) ) ;
BOOST_CHECK( !opt.get() ) ;
check_uninitialized_const(opt);
}
template<class T>
inline void check_initialized_const ( optional<T> const& opt )
{
BOOST_CHECK( opt ) ;
BOOST_CHECK( opt != 0 ) ;
BOOST_CHECK ( !!opt ) ;
BOOST_CHECK ( get_pointer(opt) ) ;
BOOST_CHECK ( opt.get() ) ;
}
template<class T>
inline void check_initialized ( optional<T>& opt )
{
BOOST_CHECK( opt ) ;
BOOST_CHECK( opt != 0 ) ;
BOOST_CHECK ( !!opt ) ;
BOOST_CHECK ( get_pointer(opt) ) ;
BOOST_CHECK ( opt.get() ) ;
check_initialized_const(opt);
}
template<class T>
inline void check_value_const ( optional<T> const& opt, T const& v, T const& z )
{
BOOST_CHECK( *opt == v ) ;
BOOST_CHECK( *opt != z ) ;
BOOST_CHECK( (*(opt.operator->()) == v) ) ;
BOOST_CHECK( *get_pointer(opt) == v ) ;
BOOST_CHECK( *opt.get() == v ) ;
}
template<class T>
inline void check_value ( optional<T>& opt, T const& v, T const& z )
{
#ifdef _MSC_VER
// For some reason, VC6.0 is creating a temporary while evaluating (*opt == v),
// so we need to turn throw on copy off first.
reset_throw_on_copy( ARG(T) ) ;
#endif
BOOST_CHECK( *opt == v ) ;
BOOST_CHECK( *opt != z ) ;
BOOST_CHECK( (*(opt.operator->()) == v) ) ;
BOOST_CHECK( *get_pointer(opt) == v ) ;
BOOST_CHECK( *opt.get() == v ) ;
check_value_const(opt,v,z);
}
//
// Basic test.
// Check ordinary functionality:
// Initialization, assignment, comparison and value-accessing.
//
template<class T>
void test_basics( T const* )
{
std::cout << std::endl ;
T z(-1);
T a(1);
// Default construction.
// 'def' state is Uninitialized.
// T::T() is not called (and it is not even defined)
optional<T> def ;
check_uninitialized(def);
// Direct initialization.
// 'oa' state is Initialized with 'a'
// T::T( T const& x ) is used.
set_pending_copy( ARG(T) ) ;
optional<T> oa ( a ) ;
check_is_not_pending_copy( ARG(T) );
check_initialized(oa);
check_value(oa,a,z);
T b(2);
optional<T> ob ;
// Value-Assignment upon Uninitialized optional.
// T::T ( T const& x ) is used.
set_pending_copy( ARG(T) ) ;
ob.reset(a) ;
check_is_not_pending_copy( ARG(T) ) ;
check_initialized(ob);
check_value(ob,a,z);
// Value-Assignment upon Initialized optional.
// This uses T::operator= ( T const& x ) directly
// on the reference returned by operator*()
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
*ob = b ;
check_is_pending_dtor( ARG(T) ) ;
check_is_pending_copy( ARG(T) ) ;
check_initialized(ob);
check_value(ob,b,z);
// Assignment initialization.
// T::T ( T const& x ) is used to copy new value.
set_pending_copy( ARG(T) ) ;
optional<T> const oa2 = oa ;
check_is_not_pending_copy( ARG(T) ) ;
check_initialized_const(oa2);
check_value_const(oa2,a,z);
// Assignment
// T::~T() is used to destroy previous value in ob.
// T::T ( T const& x ) is used to copy new value.
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
oa = ob ;
check_is_not_pending_dtor( ARG(T) ) ;
check_is_not_pending_copy( ARG(T) ) ;
check_initialized(oa);
check_value(oa,b,z);
// Uninitializing Assignment upon Initialized Optional
// T::~T() is used to destroy previous value in oa.
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
oa = def ;
check_is_not_pending_dtor( ARG(T) ) ;
check_is_pending_copy ( ARG(T) ) ;
check_uninitialized(oa);
// Uninitializing Assignment upon Uninitialized Optional
// (Dtor is not called this time)
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
oa = def ;
check_is_pending_dtor( ARG(T) ) ;
check_is_pending_copy( ARG(T) ) ;
check_uninitialized(oa);
// Deinitialization of Initialized Optional
// T::~T() is used to destroy previous value in ob.
set_pending_dtor( ARG(T) ) ;
ob.reset();
check_is_not_pending_dtor( ARG(T) ) ;
check_uninitialized(ob);
// Deinitialization of Uninitialized Optional
// (Dtor is not called this time)
set_pending_dtor( ARG(T) ) ;
ob.reset();
check_is_pending_dtor( ARG(T) ) ;
check_uninitialized(ob);
#ifdef SHOW_COMPILATION_FAIL_1
// This is illegal since 'oa2' is const.
*oa2 = oa ;
#endif
#ifdef SHOW_COMPILATION_FAIL_2
T c(3);
// Direct Value Assignment is not allowed.
// Use operator*() instead.
oa = c ;
#endif
#ifdef SHOW_COMPILATION_FAIL_3
T d(4);
// Direct Value Construction is explicit.
optional<T> oc = d ;
#endif
}
//
// Test Direct Value Manipulation
//
template<class T>
void test_direct_value_manip( T const* )
{
T x(1);
optional<T> const c_opt0(x) ;
optional<T> opt0(x);
BOOST_CHECK( c_opt0->V() == x.V() ) ;
BOOST_CHECK( opt0->V() == x.V() ) ;
BOOST_CHECK( (*c_opt0).V() == x.V() ) ;
BOOST_CHECK( (* opt0).V() == x.V() ) ;
T y(2);
*opt0 = y ;
BOOST_CHECK( (*opt0).V() == y.V() ) ;
BOOST_CHECK( x < (*opt0) ) ;
}
//
// Test Uninitialized access assert
//
template<class T>
void test_uninitialized_access( T const* )
{
optional<T> def ;
bool passed = false ;
try
{
// This should throw becasue 'def' is uninitialized
T const& n = *def ;
(n);
passed = true ;
}
catch (...) {}
BOOST_CHECK(!passed);
passed = false ;
try
{
T v(1) ; (v);
// This should throw becasue 'def' is uninitialized
*def = v ;
passed = true ;
}
catch (...) {}
BOOST_CHECK(!passed);
passed = false ;
try
{
// This should throw becasue 'def' is uninitialized
T v = *(def.operator->()) ;
(v);
passed = true ;
}
catch (...) {}
BOOST_CHECK(!passed);
}
//
// Test Direct Initialization of optional for a T with throwing copy-ctor.
//
template<class T>
void test_throwing_direct_init( T const* )
{
T a(1234);
int count = get_instance_count( ARG(T) ) ;
set_throw_on_copy( ARG(T) ) ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'a' and throw.
// 'opt' won't be constructed.
set_pending_copy( ARG(T) ) ;
optional<T> opt(a) ;
passed = true ;
}
catch ( ... ){}
BOOST_CHECK(!passed);
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) );
}
//
// Test Value Assignment to an Uninitialized optional for a T with a throwing copy-ctor
//
template<class T>
void test_throwing_val_assign_on_uninitialized( T const* )
{
T a(1234);
int count = get_instance_count( ARG(T) ) ;
set_throw_on_copy( ARG(T) ) ;
optional<T> opt ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'a' and throw.
// opt should be left uninitialized.
set_pending_copy( ARG(T) ) ;
opt.reset( a );
passed = true ;
}
catch ( ... ) {}
BOOST_CHECK(!passed);
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) );
check_uninitialized(opt);
}
//
// Test Value Reset on an Initialized optional for a T with a throwing copy-ctor
//
template<class T>
void test_throwing_val_assign_on_initialized( T const* )
{
T z(-1);
T a(1234);
T b(5678);
int count = get_instance_count( ARG(T) ) ;
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt ( b ) ;
++ count ;
check_instance_count(count, ARG(T) );
check_value(opt,b,z);
set_throw_on_copy( ARG(T) ) ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'a' and throw.
// opt should be left uninitialized (even though it was initialized)
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
opt.reset ( a ) ;
passed = true ;
}
catch ( ... ) {}
BOOST_CHECK(!passed);
-- count ;
check_is_not_pending_dtor( ARG(T) );
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) );
check_uninitialized(opt);
}
//
// Test Copy Initialization from an Initialized optional for a T with a throwing copy-ctor
//
template<class T>
void test_throwing_copy_initialization( T const* )
{
T z(-1);
T a(1234);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt (a);
int count = get_instance_count( ARG(T) ) ;
set_throw_on_copy( ARG(T) ) ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'opt' and throw.
// opt1 won't be constructed.
set_pending_copy( ARG(T) ) ;
optional<T> opt1 = opt ;
passed = true ;
}
catch ( ... ) {}
BOOST_CHECK(!passed);
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) );
// Nothing should have happened to the source optional.
check_initialized(opt);
check_value(opt,a,z);
}
//
// Test Assignment to an Uninitialized optional from an Initialized optional
// for a T with a throwing copy-ctor
//
template<class T>
void test_throwing_assign_to_uninitialized( T const* )
{
T z(-1);
T a(1234);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt0 ;
optional<T> opt1(a) ;
int count = get_instance_count( ARG(T) ) ;
set_throw_on_copy( ARG(T) ) ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'opt1.value()' into opt0 and throw.
// opt0 should be left uninitialized.
set_pending_copy( ARG(T) ) ;
opt0 = opt1 ;
passed = true ;
}
catch ( ... ) {}
BOOST_CHECK(!passed);
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) );
check_uninitialized(opt0);
}
//
// Test Assignment to an Initialized optional from an Initialized optional
// for a T with a throwing copy-ctor
//
template<class T>
void test_throwing_assign_to_initialized( T const* )
{
T z(-1);
T a(1234);
T b(5678);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt0(a) ;
optional<T> opt1(b) ;
int count = get_instance_count( ARG(T) ) ;
set_throw_on_copy( ARG(T) ) ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'opt1.value()' into opt0 and throw.
// opt0 should be left uninitialized (even though it was initialized)
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
opt0 = opt1 ;
passed = true ;
}
catch ( ... ) {}
BOOST_CHECK(!passed);
-- count ;
check_is_not_pending_dtor( ARG(T) );
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) );
check_uninitialized(opt0);
}
//
// Test swap in a no-throwing case
//
template<class T>
void test_no_throwing_swap( T const* )
{
T z(-1);
T a(1234);
T b(5678);
reset_throw_on_copy( ARG(T) ) ;
optional<T> def0 ;
optional<T> def1 ;
optional<T> opt0(a) ;
optional<T> opt1(b) ;
int count = get_instance_count( ARG(T) ) ;
using boost::swap ;
swap(def0,def1);
check_uninitialized(def0);
check_uninitialized(def1);
swap(def0,opt0);
check_uninitialized(opt0);
check_initialized(def0);
check_value(def0,a,z);
// restore def0 and opt0
swap(def0,opt0);
swap(opt0,opt1);
check_instance_count(count, ARG(T) );
check_initialized(opt0);
check_initialized(opt1);
check_value(opt0,b,z);
check_value(opt1,a,z);
}
//
// Test swap in a throwing case
//
template<class T>
void test_throwing_swap( T const* )
{
T a(1234);
T b(5678);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt0(a) ;
optional<T> opt1(b) ;
set_throw_on_copy( ARG(T) ) ;
//
// Case 1: Both Initialized.
//
bool passed = false ;
try
{
// This should attempt to swap optionals and fail at swap(X&,X&).
swap(opt0,opt1);
passed = true ;
}
catch ( ... ) {}
BOOST_CHECK(!passed);
// Assuming swap(T&,T&) has at least the basic guarantee, these should hold.
BOOST_CHECK( ( !opt0 || ( !!opt0 && ( ( *opt0 == a ) || ( *opt0 == b ) ) ) ) ) ;
BOOST_CHECK( ( !opt1 || ( !!opt1 && ( ( *opt1 == a ) || ( *opt1 == b ) ) ) ) ) ;
//
// Case 2: Only one Initialized.
//
reset_throw_on_copy( ARG(T) ) ;
opt0.reset();
opt1.reset(a);
set_throw_on_copy( ARG(T) ) ;
passed = false ;
try
{
// This should attempt to swap optionals and fail at opt0.reset(*opt1)
// opt0 should be left uninitialized and opt1 unchanged.
swap(opt0,opt1);
passed = true ;
}
catch ( ... ) {}
BOOST_CHECK(!passed);
check_uninitialized(opt0);
check_initialized(opt1);
check_value(opt1,a,b);
}
//
// This verifies relational operators.
//
template<class T>
void test_relops( T const* v )
{
reset_throw_on_copy( ARG(T) ) ;
T v0(1);
T v1(2);
T v2(2);
optional<T> def0 ;
optional<T> def1 ;
optional<T> opt0(v0);
optional<T> opt1(v1);
optional<T> opt2(v2);
#ifdef SHOW_COMPILATION_FAIL_4a
// You can compare against 0 or against another optional<>,
// but not against another value
if ( def0 == 1 ) ;
#endif
#ifdef SHOW_COMPILATION_FAIL_4b
if ( def0 != 1 ) ;
#endif
// Check identity
BOOST_CHECK ( def0 == def0 ) ;
BOOST_CHECK ( opt0 == opt0 ) ;
BOOST_CHECK ( !(def0 != def0) ) ;
BOOST_CHECK ( !(opt0 != opt0) ) ;
// If both are uininitalized they compare equal
BOOST_CHECK ( def0 == def1 ) ;
BOOST_CHECK ( !(def0 != def1) ) ;
// If only one is initialized they compare unequal
BOOST_CHECK ( def0 != opt0 ) ;
BOOST_CHECK ( !(def1 == opt1) ) ;
// If both are initialized, values are compared
BOOST_CHECK ( opt0 != opt1 ) ;
BOOST_CHECK ( opt1 == opt2 ) ;
}
void test_with_builtin_types()
{
test_basics( ARG(double) );
test_uninitialized_access( ARG(double) );
test_no_throwing_swap( ARG(double) );
test_relops( ARG(double) ) ;
}
void test_with_class_type()
{
test_basics( ARG(X) );
test_direct_value_manip( ARG(X) );
test_uninitialized_access( ARG(X) );
test_throwing_direct_init( ARG(X) );
test_throwing_val_assign_on_uninitialized( ARG(X) );
test_throwing_val_assign_on_initialized( ARG(X) );
test_throwing_copy_initialization( ARG(X) );
test_throwing_assign_to_uninitialized( ARG(X) );
test_throwing_assign_to_initialized( ARG(X) );
test_no_throwing_swap( ARG(X) );
test_throwing_swap( ARG(X) );
test_relops( ARG(X) ) ;
BOOST_CHECK ( X::count == 0 ) ;
}
int eat ( char ) { return 1 ; }
int eat ( int ) { return 1 ; }
int eat ( void const* ) { return 1 ; }
template<class T> int eat ( T ) { return 0 ; }
//
// This verifies that operator safe_bool() behaves properly.
//
template<class T>
void test_no_implicit_conversions_impl( T const& v )
{
optional<T> def ;
BOOST_CHECK ( eat(def) == 0 ) ;
}
void test_no_implicit_conversions()
{
char c = 0 ;
int i = 0 ;
void const* p = 0 ;
test_no_implicit_conversions_impl(c);
test_no_implicit_conversions_impl(i);
test_no_implicit_conversions_impl(p);
}
struct A {} ;
void test_conversions()
{
char c = 123 ;
optional<char> opt0(c);
optional<int> opt1(opt0);
BOOST_CHECK(*opt1 == static_cast<int>(c));
float f = 1.234 ;
double d = f ;
optional<float> opt2(f) ;
optional<double> opt3 ;
opt3 = opt2 ;
BOOST_CHECK(*opt3 == d);
#ifdef SHOW_COMPILATION_FAIL_5a
optional<A> opt4(opt0);
#endif
#ifdef SHOW_COMPILATION_FAIL_5b
optional<A> opt5 ;
opt5 = opt0;
#endif
}
int test_main( int, char* [] )
{
try
{
test_with_class_type();
test_with_builtin_types();
test_no_implicit_conversions();
test_conversions();
}
catch (... )
{
BOOST_ERROR("Unexpected Exception caught!");
}
return 0;
}