diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index ebcf17a..f23df09 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2003, Fernando Luis Cacciola Carballal. +// Copyright (C) 2003, 2008 Fernando Luis Cacciola Carballal. // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,6 +9,9 @@ // You are welcome to contact the author at: // fernando_cacciola@hotmail.com // +// Revisions: +// 25 Apr 2008 (improved swap) Fernando Cacciola, Niels Dekker, Thorsten Ottosen +// #ifndef BOOST_OPTIONAL_OPTIONAL_FLC_19NOV2002_HPP #define BOOST_OPTIONAL_OPTIONAL_FLC_19NOV2002_HPP @@ -19,6 +22,7 @@ #include "boost/assert.hpp" #include "boost/type.hpp" #include "boost/type_traits/alignment_of.hpp" +#include "boost/type_traits/has_nothrow_constructor.hpp" #include "boost/type_traits/type_with_alignment.hpp" #include "boost/type_traits/remove_reference.hpp" #include "boost/type_traits/is_reference.hpp" @@ -28,6 +32,7 @@ #include "boost/detail/reference_content.hpp" #include "boost/none.hpp" #include "boost/utility/compare_pointees.hpp" +#include "boost/utility/in_place_factory.hpp" #include "boost/optional/optional_fwd.hpp" @@ -571,6 +576,14 @@ class optional : public optional_detail::optional_base return *this ; } + void swap( optional & arg ) + { + // allow for Koenig lookup + using std::swap ; + swap(*this, arg); + } + + // Returns a reference to the value if this is initialized, otherwise, // the behaviour is UNDEFINED // No-throw @@ -878,44 +891,77 @@ namespace optional_detail { #define BOOST_OPTIONAL_STD_SWAP_INTRODUCED_AT_NS_SCOPE #endif -// optional's swap: -// If both are initialized, calls swap(T&, T&). If this swap throws, both will remain initialized but their values are now unspecified. -// If only one is initialized, calls U.reset(*I), THEN I.reset(). -// If U.reset(*I) throws, both are left UNCHANGED (U is kept uinitialized and I is never reset) -// If both are uninitialized, do nothing (no-throw) -template -inline -void optional_swap ( optional& x, optional& y ) -{ - if ( !x && !!y ) + template struct swap_selector; + + template<> + struct swap_selector { - x.reset(*y); - y.reset(); - } - else if ( !!x && !y ) - { - y.reset(*x); - x.reset(); - } - else if ( !!x && !!y ) - { -// GCC > 3.2 and all other compilers have the using declaration at function scope (FLC) + template + static void optional_swap ( optional& x, optional& y ) + { + bool hasX = x; + bool hasY = y; + + if ( !hasX && !hasY ) + return; + + if( !hasX ) + x = boost::in_place(); + else if ( !hasY ) + y = boost::in_place(); + + // GCC > 3.2 and all other compilers have the using declaration at function scope (FLC) #ifndef BOOST_OPTIONAL_STD_SWAP_INTRODUCED_AT_NS_SCOPE - // allow for Koenig lookup - using std::swap ; + // allow for Koenig lookup + using std::swap ; #endif - swap(*x,*y); - } -} + swap(*x,*y); + + if( !hasX ) + y = boost::none ; + else if( !hasY ) + x = boost::none ; + } + }; + + template<> + struct swap_selector + { + template + static void optional_swap ( optional& x, optional& y ) + { + if ( !x && !!y ) + { + x = *y; + y = boost::none ; + } + else if ( !!x && !y ) + { + y = *x ; + x = boost::none ; + } + else if ( !!x && !!y ) + { + // GCC > 3.2 and all other compilers have the using declaration at function scope (FLC) + #ifndef BOOST_OPTIONAL_STD_SWAP_INTRODUCED_AT_NS_SCOPE + // allow for Koenig lookup + using std::swap ; + #endif + swap(*x,*y); + } + } + }; } // namespace optional_detail +template +struct optional_swap_should_use_default_constructor : has_nothrow_default_constructor {} ; + template inline void swap ( optional& x, optional& y ) { - optional_detail::optional_swap(x,y); + optional_detail::swap_selector::value>::optional_swap(x, y); } - } // namespace boost #endif diff --git a/test/optional_test.cpp b/test/optional_test.cpp index 02ed181..8c6811b 100644 --- a/test/optional_test.cpp +++ b/test/optional_test.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2003, Fernando Luis Cacciola Carballal. +// Copyright (C) 2003, 2008 Fernando Luis Cacciola Carballal. // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,6 +9,9 @@ // You are welcome to contact the author at: // fernando_cacciola@hotmail.com // +// Revisions: +// 25 Apr 2008 (added more swap tests) +// #include #include #include @@ -16,6 +19,8 @@ #define BOOST_ENABLE_ASSERT_HANDLER #include "boost/bind/apply.hpp" // Included just to test proper interaction with boost::apply<> as reported by Daniel Wallin +#include "boost/mpl/bool.hpp" +#include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_ #include "boost/optional/optional.hpp" @@ -927,6 +932,185 @@ void test_conversions2() BOOST_CHECK(*get(&opt1) == static_cast(f)); } + +namespace optional_swap_test +{ + class default_ctor_exception : public std::exception {} ; + class copy_ctor_exception : public std::exception {} ; + class assignment_exception : public std::exception {} ; + + // + // Base class for swap test classes. Its assignment should not be called, when swapping + // optional objects. (The default std::swap would do so.) + // + class base_class_with_forbidden_assignment + { + public: + base_class_with_forbidden_assignment & operator=(const base_class_with_forbidden_assignment &) + { + BOOST_CHECK(!"The assignment should not be used while swapping!"); + throw assignment_exception(); + } + + virtual ~base_class_with_forbidden_assignment() {} + }; + + // + // Class without default constructor + // + class class_without_default_ctor : public base_class_with_forbidden_assignment + { + public: + char data; + explicit class_without_default_ctor(char arg) : data(arg) {} + }; + + // + // Class whose default constructor should not be used by optional::swap! + // + class class_whose_default_ctor_should_not_be_used : public base_class_with_forbidden_assignment + { + public: + char data; + explicit class_whose_default_ctor_should_not_be_used(char arg) : data(arg) {} + + class_whose_default_ctor_should_not_be_used() + { + BOOST_CHECK(!"This default constructor should not be used while swapping!"); + throw default_ctor_exception(); + } + }; + + // + // Class whose default constructor should be used by optional::swap. + // Its copy constructor should be avoided! + // + class class_whose_default_ctor_should_be_used : public base_class_with_forbidden_assignment + { + public: + char data; + explicit class_whose_default_ctor_should_be_used(char arg) : data(arg) { } + + class_whose_default_ctor_should_be_used() : data('\0') { } + + class_whose_default_ctor_should_be_used(const class_whose_default_ctor_should_be_used &) + { + BOOST_CHECK(!"This copy constructor should not be used while swapping!"); + throw copy_ctor_exception(); + } + }; + + void swap(class_whose_default_ctor_should_not_be_used & lhs, class_whose_default_ctor_should_not_be_used & rhs) + { + std::swap(lhs.data, rhs.data); + } + + void swap(class_whose_default_ctor_should_be_used & lhs, class_whose_default_ctor_should_be_used & rhs) + { + std::swap(lhs.data, rhs.data); + } + + void swap(class_without_default_ctor & lhs, class_without_default_ctor & rhs) + { + std::swap(lhs.data, rhs.data); + } + +} // End of namespace optional_swap_test. + +namespace boost { + + // + // Compile time tweaking on whether or not swap should use the default constructor: + // + template <> struct optional_swap_should_use_default_constructor< + ::optional_swap_test::class_whose_default_ctor_should_be_used> : mpl::true_ {} ; + + template <> struct optional_swap_should_use_default_constructor< + ::optional_swap_test::class_whose_default_ctor_should_not_be_used> : mpl::false_ {} ; +} + + +// +// Tests whether the swap function works properly for optional. +// Assumes that T has one data member, of type char. +// Returns true iff the test is passed. +// +template +bool test_swap_function( T const* ) +{ + const boost::unit_test::counter_t counter_before_test = boost::minimal_test::errors_counter(); + try + { + optional obj1; + optional obj2('a'); + + // Test if obj1 and obj2 are properly initialized. + BOOST_CHECK(!obj1); + BOOST_CHECK(!!obj2 && obj2->data == 'a'); + + // Call non-member swap + swap(obj1, obj2); + + // Test if obj1 and obj2 are really swapped + BOOST_CHECK(!!obj1 && obj1->data == 'a'); + BOOST_CHECK(!obj2); + } + catch(const std::exception &) + { + // The swap function should not throw, for our test cases. + return false ; + } + return boost::minimal_test::errors_counter() == counter_before_test ; +} + +// +// Tests whether the optional::swap member function works properly. +// Assumes that T has one data member, of type char. +// Returns true iff the test is passed. +// +template +bool test_swap_member_function( T const* ) +{ + const boost::unit_test::counter_t counter_before_test = boost::minimal_test::errors_counter(); + try + { + optional obj1; + optional obj2('a'); + + // Test if obj1 and obj2 are properly initialized. + BOOST_CHECK(!obj1); + BOOST_CHECK(!!obj2 && obj2->data == 'a'); + + // Call member swap + obj1.swap(obj2); + + // Test if obj1 and obj2 are really swapped + BOOST_CHECK(!!obj1 && obj1->data == 'a'); + BOOST_CHECK(!obj2); + } + catch(const std::exception &) + { + // The optional::swap member function should not throw, for our test cases. + return false ; + } + return boost::minimal_test::errors_counter() == counter_before_test ; +} + + +// +// Tests compile time tweaking of swap, by means of +// optional_swap_should_use_default_constructor. +// +void test_swap_tweaking() +{ + BOOST_CHECK( test_swap_function( ARG(optional_swap_test::class_without_default_ctor) ) ); + BOOST_CHECK( test_swap_function( ARG(optional_swap_test::class_whose_default_ctor_should_be_used) ) ); + BOOST_CHECK( test_swap_function( ARG(optional_swap_test::class_whose_default_ctor_should_not_be_used) ) ); + BOOST_CHECK( test_swap_member_function( ARG(optional_swap_test::class_without_default_ctor) ) ); + BOOST_CHECK( test_swap_member_function( ARG(optional_swap_test::class_whose_default_ctor_should_be_used) ) ); + BOOST_CHECK( test_swap_member_function( ARG(optional_swap_test::class_whose_default_ctor_should_not_be_used) ) ); +} + int test_main( int, char* [] ) { try @@ -936,6 +1120,7 @@ int test_main( int, char* [] ) test_no_implicit_conversions(); test_conversions1(); test_conversions2(); + test_swap_tweaking(); } catch ( ... ) {