From a2f8c2fd413ebfc3255a352a010affb7ac7f8854 Mon Sep 17 00:00:00 2001 From: Fernando Cacciola Date: Wed, 10 Sep 2003 15:39:30 +0000 Subject: [PATCH] Added functionality [SVN r19994] --- include/boost/optional.hpp | 482 +++++++++++++++++++++++++++++-------- 1 file changed, 378 insertions(+), 104 deletions(-) diff --git a/include/boost/optional.hpp b/include/boost/optional.hpp index a8257da..86e623c 100644 --- a/include/boost/optional.hpp +++ b/include/boost/optional.hpp @@ -22,8 +22,15 @@ #include "boost/config.hpp" #include "boost/assert.hpp" +#include "boost/type.hpp" #include "boost/type_traits/alignment_of.hpp" #include "boost/type_traits/type_with_alignment.hpp" +#include "boost/type_traits/remove_reference.hpp" +#include "boost/type_traits/is_reference.hpp" +#include "boost/mpl/if.hpp" +#include "boost/mpl/bool.hpp" +#include "boost/mpl/not.hpp" +#include "boost/detail/reference_content.hpp" #if BOOST_WORKAROUND(BOOST_MSVC, == 1200) // VC6.0 has the following bug: @@ -46,56 +53,279 @@ #define BOOST_OPTIONAL_NO_CONVERTING_COPY_CTOR #endif -namespace boost +namespace boost { + +class InPlaceFactoryBase ; + +namespace optional_detail { + +template +class aligned_storage { - namespace optional_detail - { - template - class aligned_storage + // Borland ICEs if unnamed unions are used for this! + union dummy_u { - // Borland ICEs if unnamed unions are used for this! - union dummy_u - { - char data[ sizeof(T) ]; - BOOST_DEDUCED_TYPENAME type_with_alignment< - ::boost::alignment_of::value >::type aligner_; - } dummy_ ; + char data[ sizeof(T) ]; + BOOST_DEDUCED_TYPENAME type_with_alignment< + ::boost::alignment_of::value >::type aligner_; + } dummy_ ; - public: + public: - void const* address() const { return &dummy_.data[0]; } - void * address() { return &dummy_.data[0]; } - } ; - } + void const* address() const { return &dummy_.data[0]; } + void * address() { return &dummy_.data[0]; } +} ; template -class optional +class types_when_isnt_ref { - typedef optional this_type ; + typedef T const& reference_const_type ; + typedef T & reference_type ; + typedef T const* pointer_const_type ; + typedef T * pointer_type ; + typedef T const& argument_type ; +} ; +template +class types_when_is_ref +{ + typedef BOOST_DEDUCED_TYPENAME remove_reference::type raw_type ; - typedef optional_detail::aligned_storage storage_type ; + typedef raw_type& reference_const_type ; + typedef raw_type& reference_type ; + typedef raw_type* pointer_const_type ; + typedef raw_type* pointer_type ; + typedef raw_type& argument_type ; +} ; - typedef void (this_type::*unspecified_bool_type)(); +template +class optional_base +{ + private : + + typedef BOOST_DEDUCED_TYPENAME detail::make_reference_content::type internal_type ; + + typedef aligned_storage storage_type ; + + typedef types_when_isnt_ref types_when_not_ref ; + typedef types_when_is_ref types_when_ref ; + + typedef optional_base this_type ; + + protected : + + typedef mpl::true_ is_reference_tag ; + typedef mpl::false_ is_not_reference_tag ; + + typedef BOOST_DEDUCED_TYPENAME is_reference::type is_reference_predicate ; - public : + typedef BOOST_DEDUCED_TYPENAME mpl::if_::type types ; + + typedef bool (this_type::*unspecified_bool_type)() const; - typedef T value_type ; + typedef BOOST_DEDUCED_TYPENAME types::reference_type reference_type ; + typedef BOOST_DEDUCED_TYPENAME types::reference_const_type reference_const_type ; + typedef BOOST_DEDUCED_TYPENAME types::pointer_type pointer_type ; + typedef BOOST_DEDUCED_TYPENAME types::pointer_const_type pointer_const_type ; + typedef BOOST_DEDUCED_TYPENAME types::argument_type argument_type ; // Creates an optional uninitialized. // No-throw - optional () + optional_base () : m_initialized(false) {} // Creates an optional initialized with 'val'. // Can throw if T::T(T const&) does - explicit optional ( T const& val ) + optional_base ( argument_type val ) : m_initialized(false) { construct(val); } + // Creates a deep copy of another optional + // Can throw if T::T(T const&) does + optional_base ( optional_base const& rhs ) + : + m_initialized(false) + { + if ( rhs.is_initialized() ) + construct(rhs.get_impl()); + } + + template + explicit optional_base ( Expr const& expr, Expr const* tag ) + : + m_initialized(false) + { + construct(expr,tag); + } + + // No-throw (assuming T::~T() doesn't) + ~optional_base() { destroy(is_reference_predicate()) ; } + + // Assigns from another optional (deep-copies the rhs value) + // Basic Guarantee: If T::T( T const& ) throws, this is left UNINITIALIZED + optional_base& operator= ( optional_base const& rhs ) + { + destroy(is_reference_predicate()); // no-throw + + if ( rhs.is_initialized() ) + { + // An exception can be thrown here. + // It it happens, THIS will be left uninitialized. + assign(rhs.get_impl(), is_reference_predicate() ); + } + return *this ; + } + + // Assigns from a T (deep-copies the rhs value) + // Basic Guarantee: If T::( T const& ) throws, this is left UNINITIALIZED + optional_base& operator= ( argument_type val ) + { + destroy(is_reference_predicate()); // no-throw + + // An exception can be thrown here. + // It it happens, THIS will be left uninitialized. + assign(val, is_reference_predicate() ); + + return *this ; + } + + public : + + // Destroys the current value, if any, leaving this UNINITIALIZED + // No-throw (assuming T::~T() doesn't) + void reset() + { + destroy(is_reference_predicate()); + } + + // Replaces the current value -if any- with 'val' + // Basic Guarantee: If T::T( T const& ) throws this is left UNINITIALIZED. + void reset ( argument_type val ) + { + destroy(is_reference_predicate()); + assign(val, is_reference_predicate() ); + } + + // Returns a pointer to the value if this is initialized, otherwise, + // returns NULL. + // No-throw + pointer_const_type get_ptr() const { return m_initialized ? get_ptr_impl() : 0 ; } + pointer_type get_ptr() { return m_initialized ? get_ptr_impl() : 0 ; } + + bool is_initialized() const { return m_initialized ; } + + protected : + + void construct ( argument_type val ) + { + new (m_storage.address()) internal_type(val) ; + m_initialized = true ; + } + + template + void construct ( Expr const& expr, void const* ) + { + new (m_storage.address()) internal_type(expr) ; + m_initialized = true ; + } + + template + void construct ( Expr const& factory, InPlaceFactoryBase const* ) + { + BOOST_STATIC_ASSERT ( mpl::not_::value ) ; + + boost::type selector ; + factory(selector,m_storage.address()) ; + m_initialized = true ; + } + + + // NOTE: If T is of reference type, assignment must be disallowed, but optional's assignment uses T's copy-ctor + // so the following overload is needed to filter out the case of T being a reference and issue an error. + void assign ( argument_type val, is_not_reference_tag ) + { + new (m_storage.address()) internal_type(val) ; + m_initialized = true ; + } + + void destroy( is_not_reference_tag ) + { + if ( m_initialized ) + { + get_impl().~T() ; + m_initialized = false ; + } + } + + void destroy( is_reference_tag ) + { + m_initialized = false ; + } + + unspecified_bool_type safe_bool() const { return m_initialized ? &this_type::is_initialized : 0 ; } + + reference_const_type get_impl() const { return dereference(get_object(), is_reference_predicate() ) ; } + reference_type get_impl() { return dereference(get_object(), is_reference_predicate() ) ; } + + pointer_const_type get_ptr_impl() const { return cast_ptr(get_object(), is_reference_predicate() ) ; } + pointer_type get_ptr_impl() { return cast_ptr(get_object(), is_reference_predicate() ) ; } + + private : + + // internal_type can be either T or reference_content + internal_type const* get_object() const { return static_cast(m_storage.address()); } + internal_type * get_object() { return static_cast (m_storage.address()); } + + // reference_content lacks an implicit conversion to T&, so the following is needed to obtain a proper reference. + reference_const_type dereference( internal_type const* p, is_not_reference_tag ) const { return *p ; } + reference_type dereference( internal_type* p, is_not_reference_tag ) { return *p ; } + reference_const_type dereference( internal_type const* p, is_reference_tag ) const { return p->get() ; } + reference_type dereference( internal_type* p, is_reference_tag ) { return p->get() ; } + + // If T is of reference type, trying to get a pointer to the held value must result in a compile-time error. + // Decent compilers should disallow conversions from reference_content* to T*, but just in case, + // the following olverloads are used to filter out the case and guarantee an error in case of T being a reference. + pointer_const_type cast_ptr( internal_type const* p, is_not_reference_tag ) const { return p ; } + pointer_type cast_ptr( internal_type * p, is_not_reference_tag ) { return p ; } + + bool m_initialized ; + storage_type m_storage ; +} ; + +} // namespace optional_detail + +template +class optional : public optional_detail::optional_base +{ + typedef optional_detail::optional_base base ; + + typedef BOOST_DEDUCED_TYPENAME base::unspecified_bool_type unspecified_bool_type ; + + public : + + typedef optional this_type ; + + typedef T value_type ; + + typedef BOOST_DEDUCED_TYPENAME base::reference_type reference_type ; + typedef BOOST_DEDUCED_TYPENAME base::reference_const_type reference_const_type ; + typedef BOOST_DEDUCED_TYPENAME base::pointer_type pointer_type ; + typedef BOOST_DEDUCED_TYPENAME base::pointer_const_type pointer_const_type ; + typedef BOOST_DEDUCED_TYPENAME base::argument_type argument_type ; + + typedef BOOST_DEDUCED_TYPENAME base::is_reference_predicate is_reference_predicate ; + + // Creates an optional uninitialized. + // No-throw + optional() : base() {} + + // Creates an optional initialized with 'val'. + // Can throw if T::T(T const&) does + optional ( argument_type val ) : base(val) {} + #ifndef BOOST_OPTIONAL_NO_CONVERTING_COPY_CTOR // NOTE: MSVC needs templated versions first @@ -105,25 +335,24 @@ class optional template explicit optional ( optional const& rhs ) : - m_initialized(false) + base() { - if ( rhs ) - construct(*rhs); + BOOST_STATIC_ASSERT ( mpl::not_::value ) ; + + if ( rhs.is_initialized() ) + construct(rhs.get()); } #endif // Creates a deep copy of another optional // Can throw if T::T(T const&) does - optional ( optional const& rhs ) - : - m_initialized(false) - { - if ( rhs ) - construct(*rhs); - } + optional ( optional const& rhs ) : base(rhs) {} + + template + explicit optional ( Expr const& expr ) : base(expr,&expr) {} // No-throw (assuming T::~T() doesn't) - ~optional() { destroy() ; } + ~optional() {} #ifndef BOOST_OPTIONAL_NO_CONVERTING_ASSIGNMENT // Assigns from another convertible optional (converts && deep-copies the rhs value) @@ -132,13 +361,15 @@ class optional template optional& operator= ( optional const& rhs ) { - destroy(); // no-throw + BOOST_STATIC_ASSERT ( mpl::not_::value ) ; - if ( rhs ) + destroy(is_reference_predicate()); // no-throw + + if ( rhs.is_initialized() ) { // An exception can be thrown here. // It it happens, THIS will be left uninitialized. - construct(*rhs); + assign(rhs.get(), is_reference_predicate() ); } return *this ; } @@ -148,95 +379,100 @@ class optional // Basic Guarantee: If T::T( T const& ) throws, this is left UNINITIALIZED optional& operator= ( optional const& rhs ) { - destroy(); // no-throw - - if ( rhs ) - { - // An exception can be thrown here. - // It it happens, THIS will be left uninitialized. - construct(*rhs); - } + this->base::operator= ( rhs ) ; return *this ; } - // Destroys the current value, if any, leaving this UNINITIALIZED - // No-throw (assuming T::~T() doesn't) - void reset() + // Assigns from a T (deep-copies the rhs value) + // Basic Guarantee: If T::( T const& ) throws, this is left UNINITIALIZED + optional& operator= ( argument_type val ) { - destroy(); + this->base::operator= ( val ) ; + return *this ; } - // Replaces the current value -if any- with 'val' - // Basic Guarantee: If T::T( T const& ) throws this is left UNINITIALIZED. - void reset ( T const& val ) - { - destroy(); - construct(val); - } - - // Returns a pointer to the value if this is initialized, otherwise, - // returns NULL. - // No-throw - T const* get() const { return m_initialized ? static_cast(m_storage.address()) : 0 ; } - T* get() { return m_initialized ? static_cast (m_storage.address()) : 0 ; } - - // Returns a pointer to the value if this is initialized, otherwise, - // the behaviour is UNDEFINED - // No-throw - T const* operator->() const { BOOST_ASSERT(m_initialized) ; return static_cast(m_storage.address()) ; } - T* operator->() { BOOST_ASSERT(m_initialized) ; return static_cast (m_storage.address()) ; } - // Returns a reference to the value if this is initialized, otherwise, // the behaviour is UNDEFINED // No-throw - T const& operator *() const { BOOST_ASSERT(m_initialized) ; return *static_cast(m_storage.address()) ; } - T& operator *() { BOOST_ASSERT(m_initialized) ; return *static_cast (m_storage.address()) ; } + reference_const_type get() const { BOOST_ASSERT(this->is_initialized()) ; return this->get_impl(); } + reference_type get() { BOOST_ASSERT(this->is_initialized()) ; return this->get_impl(); } + + // Returns a pointer to the value if this is initialized, otherwise, + // the behaviour is UNDEFINED + // No-throw + pointer_const_type operator->() const { BOOST_ASSERT(this->is_initialized()) ; return this->get_ptr_impl() ; } + pointer_type operator->() { BOOST_ASSERT(this->is_initialized()) ; return this->get_ptr_impl() ; } + + // Returns a reference to the value if this is initialized, otherwise, + // the behaviour is UNDEFINED + // No-throw + reference_const_type operator *() const { return this->get() ; } + reference_type operator *() { return this->get() ; } // implicit conversion to "bool" // No-throw - operator unspecified_bool_type() const { return m_initialized ? &this_type::destroy : 0 ; } + operator unspecified_bool_type() const { return this->safe_bool() ; } // This is provided for those compilers which don't like the conversion to bool // on some contexts. - bool operator!() const { return !m_initialized ; } - - private : - - void construct ( T const& val ) - { - new (m_storage.address()) T(val) ; - m_initialized = true ; - } - - void destroy() - { - if ( m_initialized ) - { - get()->~T() ; - m_initialized = false ; - } - } - - bool m_initialized ; - storage_type m_storage ; + bool operator!() const { return !this->is_initialized() ; } } ; +// Returns a reference to the value if this is initialized, otherwise, the behaviour is UNDEFINED. +// No-throw +template +inline +BOOST_DEDUCED_TYPENAME optional::reference_const_type +get ( optional const& opt ) +{ + return opt.get() ; +} + +template +inline +BOOST_DEDUCED_TYPENAME optional::reference_type +get ( optional& opt ) +{ + return opt.get() ; +} + // Returns a pointer to the value if this is initialized, otherwise, returns NULL. // No-throw template inline -T const* get_pointer ( optional const& opt ) +BOOST_DEDUCED_TYPENAME optional::pointer_const_type +get ( optional const* opt ) { - return opt.get() ; + return opt->get_ptr() ; } template inline -T* get_pointer ( optional& opt ) +BOOST_DEDUCED_TYPENAME optional::pointer_type +get ( optional* opt ) { - return opt.get() ; + return opt->get_ptr() ; } +// Returns a pointer to the value if this is initialized, otherwise, returns NULL. +// No-throw +template +inline +BOOST_DEDUCED_TYPENAME optional::pointer_const_type +get_pointer ( optional const& opt ) +{ + return opt.get_ptr() ; +} + +template +inline +BOOST_DEDUCED_TYPENAME optional::pointer_type +get_pointer ( optional& opt ) +{ + return opt.get_ptr() ; +} + + // template bool equal_pointees(OP const& x, OP const& y); // // Being OP a model of OptionalPointee (either a pointer or an optional): @@ -252,18 +488,53 @@ bool equal_pointees ( OptionalPointee const& x, OptionalPointee const& y ) return (!x) != (!y) ? false : ( !x ? true : (*x) == (*y) ) ; } -// optional's operator == and != have deep-semantics (compare values). -// WARNING: This is UNLIKE pointers. Use equal_pointees() in generic code instead. +// template bool less_pointees(OP const& x, OP const& y); +// +// Being OP a model of OptionalPointee (either a pointer or an optional): +// +// If y has not a valid pointee, returns false. +// ElseIf x has not a valid pointee, returns true. +// ElseIf both x and y have valid pointees, returns the result of (*x < *y) +// No-throw +template +inline +bool less_pointees ( OptionalPointee const& x, OptionalPointee const& y ) +{ + return !y ? false : ( !x ? true : (*x) < (*y) ) ; +} + +// optional's relational operators ( ==, !=, <, >, <=, >= ) have deep-semantics (compare values). +// WARNING: This is UNLIKE pointers. Use equal_pointees()/less_pointess() in generic code instead. + template inline bool operator == ( optional const& x, optional const& y ) { return equal_pointees(x,y); } +template +inline +bool operator < ( optional const& x, optional const& y ) +{ return less_pointees(x,y); } + template inline bool operator != ( optional const& x, optional const& y ) { return !( x == y ) ; } +template +inline +bool operator > ( optional const& x, optional const& y ) +{ return y < x ; } + +template +inline +bool operator <= ( optional const& x, optional const& y ) +{ return !( y < x ) ; } + +template +inline +bool operator >= ( optional const& x, optional const& y ) +{ return !( x < y ) ; } // // The following swap implementation follows the GCC workaround as found in @@ -271,9 +542,11 @@ bool operator != ( optional const& x, optional const& y ) // namespace optional_detail { -#if defined(__GNUC__) && (BOOST_WORKAROUND(__GNUC__, <= 2) || __GNUC__ == 3 && BOOST_WORKAROUND(__GNUC_MINOR__, < 3)) - // workaround for GCC earlier than version 3.3 (JM): +// GCC <= 3.2 gets the using declaration at namespace scope (FLC) +#if BOOST_WORKAROUND(__GNUC__, <= 3) && __GNUC_MINOR__ <= 2 + // workaround for GCC (JM): using std::swap; +#define BOOST_OPTIONAL_STD_SWAP_INTRODUCED_AT_NS_SCOPE #endif // optional's swap: @@ -296,7 +569,8 @@ void optional_swap ( optional& x, optional& y ) } else if ( !!x && !!y ) { -#if !(defined(__GNUC__) && (BOOST_WORKAROUND(__GNUC__, <= 2) || __GNUC__ == 3 && BOOST_WORKAROUND(__GNUC_MINOR__, < 3))) +// 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