diff --git a/doc/optional.html b/doc/optional.html index 5512115..d29bd07 100644 --- a/doc/optional.html +++ b/doc/optional.html @@ -20,6 +20,7 @@ HREF="../../../boost/optional/optional.hpp">boost/optional/optional.hpp>
Semantics
Examples
Optional references
+
Rebinding semantics for assignment of optional references
In-Place Factories
A note about optional<bool>
Exception Safety Guarantees
@@ -35,7 +36,7 @@ HREF="../../../boost/optional/optional.hpp">boost/optional/optional.hpp>

Consider these functions which should return a value but which might not have a value to return:

-
(A) double sqrt( double n );
+
(A) double sqrt(double n );
 (B) char get_async_input();
 (C) point polygon::get_any_point_effectively_inside();

There are different approaches to the issue of not having a value to return.

@@ -100,11 +101,12 @@ if ( p.second ) and neither default nor value initialization applies, it is said that the object is uninitialized. Its actual value exist but has an indeterminate inital value (c.f. 8.5.9).
- optional<T> intends to formalize the notion of initialization/no-initialization + optional<T> intends to formalize the notion of initialization +(or loack of it) allowing a program to test whether an object has been initialized and stating that access to the value of an uninitialized object is undefined behaviour. That is, when a variable is declared as optional<T> and no initial value is given, - the variable is formally uninitialized. A formally uninitialized optional object has conceptually + the variable is formally uninitialized. A formally uninitialized optional object has conceptually no value at all and this situation can be tested at runtime. It is formally undefined behaviour to try to access the value of an uninitialized optional. An uninitialized optional can be assigned a value, in which case its initialization state changes to initialized. Furthermore, given the formal treatment of initialization states in optional objects, it is even possible to reset an optional to uninitialized.

@@ -122,8 +124,8 @@ if ( p.second ) Using the Boost.Variant library, this model can be implemented in terms of boost::variant<T,nil_t>.
There is precedence for a discriminated union as a model for an optional value: the - Haskell Maybe builtin type constructor, - thus a discriminated union T+nil_t serves as a conceptual foundation.

+ Haskell Maybe builtin type constructor. +Thus, a discriminated union T+nil_t serves as a conceptual foundation.

A variant<T,nil_t> follows naturally from the traditional idiom of extending the range of possible values adding an additional sentinel value with the special meaning of Nothing. However, this additional Nothing value is largely irrelevant for our purpose @@ -180,33 +182,31 @@ object.

Direct Value Construction via copy: To introduce a formally initialized wrapped object whose value is obtained as a copy of some object.

-

Deep Copy Construction: To obtain a different yet equivalent wrapped +

Deep Copy Construction: To obtain a new yet equivalent wrapped object.

-

Direct Value Assignment (upon initialized): To assign the wrapped object a value obtained -as a copy of some object.

+

Direct Value Assignment (upon initialized): To assign a value to the wrapped object.

Direct Value Assignment (upon uninitialized): To initialize the wrapped object with a value obtained as a copy of some object.

-

Assignnment (upon initialized): To assign the wrapped object a value obtained as a copy -of another wrapper's object.

+

Assignnment (upon initialized): To assign to the wrapped object the value +of another wrapped object.

Assignnment (upon uninitialized): To initialize the wrapped object -with value obtained as a copy -of another wrapper's object.

+with value of another wrapped object.

Deep Relational Operations (when supported by the type T): To compare wrapped object values taking into account the presence of uninitialized -operands.

+states.

Value access: To unwrap the wrapped object.

Initialization state query: To determine if the object is formally initialized or not.

-

Swap: To exchange wrapper's objects. (with whatever exception safety +

Swap: To exchange wrapped objects. (with whatever exception safety guarantiees are provided by T's swap).

De-initialization: To release the wrapped object (if any) and leave @@ -313,7 +313,7 @@ class optional optional () ; - optional ( detail::none_t ) ; + optional ( none_t ) ; optional ( T const& v ) ; @@ -325,7 +325,7 @@ class optional template<class TypedInPlaceFactory> explicit optional ( TypedInPlaceFactory const& f ) ; - optional& operator = ( detail::none_t ) ; + optional& operator = ( none_t ) ; optional& operator = ( T const& v ) ; @@ -427,7 +427,7 @@ assert ( !def ) ;


-
optional<T>::optional( detail::none_t );
+
optional<T>::optional( none_t );

Effect: Constructs an optional uninitialized.

Postconditions: *this is uninitialized.

@@ -436,11 +436,12 @@ assert ( !def ) ;

T's default constructor is not called.
The -expression boost::none denotes an instance of boost::detail::none_t that can be +expression boost::none denotes an instance of boost::none_t that can be used as the parameter.

Example:

+
#include <boost/none.hpp>
optional<T> n(none) ;
 assert ( !n ) ;
@@ -468,7 +469,7 @@ assert ( *opt == v ) ;
-
optional<T&>::optional( T ref )
+
optional<T&>::optional( T& ref )

Effect: Directly-Constructs an optional.

Postconditions: *this is initialized and its value is an @@ -522,12 +523,11 @@ assert ( init2 == init ) ;

Effect: Copy-Constructs an optional.

Postconditions: If rhs is initialized, *this is initialized -and its value is a copy of the internal wrapper holding the references in rhs; else *this +and its value is another reference to the same object referenced by *rhs; else *this is uninitialized.

Throws: Nothing.

-

Notes: If rhs is initialized, the internal wrapper will be -copied and just like true references, both *this and rhs will -referr to the same object (will alias).

+

Notes: If rhs is initialized, both *this and *rhs will +refeer to the same object (they alias).

Example:

optional<T&> uninit ;
@@ -542,6 +542,13 @@ assert ( *init == v ) ;
 
 optional<T> init2 ( init ) ;
 assert ( *init2 == v ) ;
+
+v = 3 ;
+
+assert ( *init  == 3 ) ;
+assert ( *init2 == 3 ) ;
+
+
 
@@ -615,22 +622,55 @@ assert ( *y == v ) ;

Effect: Assigns the value 'rhs' to an optional.

Postconditions: *this is initialized and its value is a copy of rhs.

-

Throws: Whatever T::T( T const& ) throws.

-

Notes: If *this was initialized, it is first reset to uninitialized -using T::~T(), then T::T(rhs) is called.

-

Exception Safety: Basic: Exceptions can only be thrown during T::T( T const& ); -in that case, *this is left uninitialized. -

+

Throws: Whatever T::operator=( T const& ) or T::T(T conbst&) throws.

+

Notes: If *this was initialized, T's assignment operator is +used, otherwise, its copy-contructor is used.

+

Exception Safety: In the event of an exception, the initialization +state of *this is unchanged and its value unspecified as far as optional +is concerned (it is up to T's operator=()) [If *this is initially +uninitialized and T's copy constructor fails, *this is left +properly uninitialized]

Example:

T x;
+optional<T> def ;
 optional<T> opt(x) ;
 
 T y;
+def = y ;
+assert ( *def == y ) ;
 opt = y ;
-assert ( *opt == y ) ;
-// previous value (copy of 'v') destroyed from within 'opt'.
+assert ( *opt == y ) ;
+
+
+
+ +
optional<T&>& optional<T&>::operator= ( T& const& rhs ) ;
+
+

Effect: (Re)binds thee wrapped reference.

+

Postconditions: *this is initialized +and it references the same object referenced by rhs.

+

Notes: If *this was initialized, is is rebound to the +new object. See here for details on this behaviour.

+

Example:

+
+
int a = 1 ;
+int b = 2 ;
+T& ra = a ;
+T& rb = b ;
+optional<int&> def ;
+optional<int&> opt(ra) ;
+
+def = rb ; // binds 'def' to 'b' through 'rb'
+assert ( *def == b ) ;
+*def = a ; // changes the value of 'b' to a copy of the value of 'a'
+assert ( b == a ) ;
+int c = 3;
+int& rc = c ;
+opt = rc ; // REBINDS to 'c' through 'rc'
+c = 4 ;
+assert ( *opt == 4 ) ;
 
@@ -644,21 +684,25 @@ assert ( *opt == y ) ; and its value is a copy of the value of rhs; else *this is uninitialized.

-

Throws: Whatever T::T( T const& ) throws.

-

Notes: If *this was initialized, it is first reset to uninitialized -using T::~T(), then T::T( T const& ) is called if rhs is initialized. -

-

Exception Safety: Basic: Exceptions can only be thrown during T::T( T const& ); -in that case, *this is left uninitialized. +

Throws: Whatever T::operator( T const&) or  T::T( T const& ) throws.

+

Notes: If both *this and rhs are initially initialized, +T's assignment operator is used. If *this is initially initialized but +rhs is uinitialized, T's destructor is called. If *this is initially +uninitialized but rhs is initialized, T's copy constructor is called.

+

Exception Safety: In the event of an exception, the initialization +state of *this is unchanged and its value unspecified as far as optional +is concerned (it is up to T's operator=()) [If *this is initially +uninitialized and T's copy constructor fails, *this is left +properly uninitialized]

Example:

T v;
 optional<T> opt(v);
-optional<T> uninit ;
+optional<T> def ;
 
-opt = uninit ;
-assert ( !opt ) ;
+opt = def ;
+assert ( !def ) ;
 // previous value (copy of 'v') destroyed from within 'opt'.
 
 
@@ -667,6 +711,40 @@ assert ( !opt ) ;
+
optional<T&> & optional<T&>::operator= ( optional<T&> const& rhs ) ;
+
+

Effect: (Re)binds thee wrapped reference.

+

Postconditions: If *rhs is initialized, *this is initialized +and it references the same object referenced by *rhs; otherwise, *this +is uninitialized (and references no object).

+

Notes: If *this was initialized and so is *rhs, this +is is rebound to the new object. See here for details on this behaviour.

+

Example:

+
+
int a = 1 ;
+int b = 2 ;
+T& ra = a ;
+T& rb = b ;
+optional<int&> def ;
+optional<int&> ora(ra) ;
+optional<int&> orb(rb) ;
+
+def = orb ; // binds 'def' to 'b' through 'rb' wrapped within 'orb'
+assert ( *def == b ) ;
+*def = ora ; // changes the value of 'b' to a copy of the value of 'a'
+assert ( b == a ) ;
+int c = 3;
+int& rc = c ;
+optional<int&> orc(rc) ;
+ora = orc ; // REBINDS ora to 'c' through 'rc'
+c = 4 ;
+assert ( *ora == 4 ) ;
+
+
+
+ +
+
template<U> optional& optional<T (not a ref)>::operator= ( optional<U> const& rhs ) ;

Effect: Assigns another convertible optional to an optional.

@@ -674,14 +752,17 @@ assert ( !opt ) ; and its value is a copy of the value of rhs converted to type T; else *this is uninitialized.

-

Throws: Whatever T::T( U const& ) throws.

-

Notes: If *this was initialized, it is first reset to uninitialized -using T::~T(), then T::T( U const& ) is called if rhs is initialized, -which requires a valid conversion from U to T. -

-

Exception Safety: Basic: Exceptions can only be thrown during T::T( U const& ); -in that case, *this is left uninitialized. +

Throws: Whatever T::operator=( U const& ) or T::T( U const& ) throws.

+

Notes: If both *this and rhs are initially initialized, +T's assignment operator (from U) is used. If *this is initially initialized but +rhs is uinitialized, T's destructor is called. If *this is initially +uninitialized but rhs is initialized, T's converting constructor (from U) is called.

+

Exception Safety: In the event of an exception, the initialization +state of *this is unchanged and its value unspecified as far as optional +is concerned (it is up to T's operator=()) [If *this is initially +uninitialized and T's converting constructor fails, *this is left +properly uninitialized]

Example:

T v;
@@ -1118,7 +1199,7 @@ class Fred
 

Optional references

-

This library allow the template parameter T to be of reference type: T&, and +

This library allows the template parameter T to be of reference type: T&, and to some extent, T const&.

However, since references are not real objects some restrictions apply and @@ -1141,6 +1222,85 @@ value, a true real reference is stored so aliasing will ocurr:

than the reference itself. +
+

Rebinding semantics for assignment of optional +references

+

If you assign to an uninitialized optional<T&> the effect is to bind (for the first time) to the object. +Clearly, there is no other choice.

+
int x = 1 ;
+int& rx = x ;
+optional<int&> ora ;
+optional<int&> orb(x) ;
+ora = orb ; // now 'ora' is bound to 'x' through 'rx'
+*ora = 2 ; // Changes value of 'x' through 'ora'
+assert(x==2); 
+
+

If you assign to a bare C++ reference, the assignment is forwarded to the +referenced object; it's value changes but the reference is never rebound.

+
int a = 1 ;
+int& ra = a ;
+int b = 2 ;
+int& rb = b ;
+ra = rb ; // Changes the value of 'a' to 'b'
+assert(a==b);
+b = 3 ;
+assert(ra!=b); // 'ra' is not rebound to 'b'
+
+

Now, if you assign to an initialized optional<T&>, the effect is to +rebind to the new object instead of assigning the referee. This is unlike +bare C++ references.

+
int a = 1 ;
+int b = 2 ;
+int& ra = a ;
+int& rb = b ;
+optional<int&> ora(ra) ;
+optional<int&> orb(rb) ;
+ora = orb ; // 'ora' is rebound to 'b'
+*ora = 3 ; // Changes value of 'b' (not 'a')
+assert(a==1); 
+assert(b==3); 
+
+

Rationale:

+

Rebinding semantics for the assignment of initialized optional +references has been choosen to provide consistency among initialization +states even at the expense of lack of consistency with the semantics of bare +C++ references.
+It is true that optional<U> strives to behave as much as possible as U does +whenever it is initialized; but in the case when U is T&, doing so would result +in incosistent behaviour w.r.t to the lvalue initialization state.

+

Imagine optional<T&> fordwarding assignment to the referenced object (thus +changing the referenced object value but not rebinding), and consider the +following code :

+
  optional<int&> a = get();
+  int x = 1 ;
+  int& rx = x ;
+  optional<int&> b(rx);
+  a = b ;
+
+

What does the assignment do?
+If 'a' is uninitialized, the answer is clear: it binds to 'x' (we now have +another reference to 'x').
+But what if 'a' is already initiliazed? it would change the value of the +referenced object (whatever that is); which is inconsistent with the other +possible case.

+

If optional<T&> would assign just like T& does, you would never be able to +use Optional's assignment without explicitely handling the previous +initialization state unless your code is capable of functioning whether after +the assignment, 'a' +aliases the same object as 'b' or not.

+

That is, you would have to discriminate in order to be consistency.
+
+If in your code rebinding to another object is not an option, then is very +likely that binding for the fist time isn't either. In such case, assignment to +an uninitialized optional<T&> shall be prohibited. It is quite +possible that in such scenario the precondition that the lvalue must be already +initialized exist. If it doesn't, then binding for the first time is OK while +rebinding is not which is IMO +very unlikely.
+In such scenario, you can assign the value itself directly, as in:

+
assert(!!opt);
+*opt=value;  
+

In-Place Factories

@@ -1152,7 +1312,7 @@ type to be Copy Constructible constructed object, often temporary, just to follow the copy from:

struct X
 {
-  X ( int, std::string ) ;
+  X ( int, std:::string ) ;
 } ;
class W
 {
@@ -1222,7 +1382,7 @@ public:
 {
   // Wrapped object constructed in-place via a TypedInPlaceFactory.
   // No temporary created.
-  W ( TypedInPlaceFactory2<X,int,std::string>(123,"hello")) ;
+  W ( TypedInPlaceFactory2<X,int,std::string&rt;(123,"hello")) ;
 }
 

The factories are divided in two groups:

    @@ -1303,7 +1463,7 @@ of the assignment methods:

    InPlaceFactory const& )
  • template<class TypedInPlaceFactory> optional<T>::operator= ( TypedInPlaceFactory const& )
  • -
  • optional<T>::reset ( T const& )
  • +
  • optional<T>:::reset ( T const&)

Can only guarantee the basic exception safety: The lvalue optional is left uninitialized if an exception is thrown (any previous value is first destroyed using T::~T())

@@ -1320,11 +1480,11 @@ for T::T ( T const& ), you know that optional's assignment and reset has the // Case 1: Exception thrown during assignment. // T v0(123); -optional<T> opt0(v0); +optional<T> opt0(v0); try {   T v1(456); -  optional<T> opt1(v1); +  optional<T> opt1(v1);   opt0 = opt1 ;   // If no exception was thrown, assignment succeeded. @@ -1340,7 +1500,7 @@ catch(...) // Case 2: Exception thrown during reset(v) // T v0(123); -optional<T> opt(v0); +optional<T> opt(v0); try {   T v1(456); @@ -1432,8 +1592,8 @@ T is not required to be LICENSE_1_0.txt or copy at @@ -1443,4 +1603,4 @@ the latest version of this file can be found at www.boost.org, and the boost discussion lists

- + \ No newline at end of file diff --git a/include/boost/none.hpp b/include/boost/none.hpp index e533e05..693dbdf 100644 --- a/include/boost/none.hpp +++ b/include/boost/none.hpp @@ -12,7 +12,7 @@ #ifndef BOOST_NONE_17SEP2003_HPP #define BOOST_NONE_17SEP2003_HPP -#include "boost/detail/none_t.hpp" +#include "boost/none_t.hpp" // NOTE: Borland users have to include this header outside any precompiled headers // (bcc<=5.64 cannot include instance data in a precompiled header) @@ -21,7 +21,7 @@ namespace boost { namespace { -detail::none_t const none = ((detail::none_t)0) ; +none_t const none = ((none_t)0) ; } diff --git a/include/boost/none_t.hpp b/include/boost/none_t.hpp new file mode 100644 index 0000000..4b97e20 --- /dev/null +++ b/include/boost/none_t.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2003, 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 +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// fernando_cacciola@hotmail.com +// +#ifndef BOOST_NONE_T_17SEP2003_HPP +#define BOOST_NONE_T_17SEP2003_HPP + +namespace boost { + +namespace detail { struct none_helper{}; } + +typedef int detail::none_helper::*none_t ; + +} // namespace boost + +#endif + diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 7379d80..99f3af0 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -26,7 +26,7 @@ #include "boost/mpl/bool.hpp" #include "boost/mpl/not.hpp" #include "boost/detail/reference_content.hpp" -#include "boost/detail/none_t.hpp" +#include "boost/none_t.hpp" #include "boost/utility/compare_pointees.hpp" #if BOOST_WORKAROUND(BOOST_MSVC, == 1200) @@ -167,7 +167,7 @@ class optional_base : public optional_tag // Creates an optional uninitialized. // No-throw - optional_base ( detail::none_t const& ) + optional_base ( none_t const& ) : m_initialized(false) {} @@ -208,32 +208,40 @@ class optional_base : public optional_tag ~optional_base() { destroy() ; } // Assigns from another optional (deep-copies the rhs value) - // Basic Guarantee: If T::T( T const& ) throws, this is left UNINITIALIZED void assign ( optional_base const& rhs ) + { + if (is_initialized()) + { + if ( rhs.is_initialized() ) + assign_value(rhs.get_impl(), is_reference_predicate() ); + else destroy(); + } + else { - destroy(); if ( rhs.is_initialized() ) construct(rhs.get_impl()); } + } // Assigns from a T (deep-copies the rhs value) - // Basic Guarantee: If T::( T const& ) throws, this is left UNINITIALIZED void assign ( argument_type val ) - { - destroy(); - construct(val); - } + { + if (is_initialized()) + assign_value(val, is_reference_predicate() ); + else construct(val); + } // Assigns from "none", destroying the current value, if any, leaving this UNINITIALIZED // No-throw (assuming T::~T() doesn't) - void assign ( detail::none_t const& ) { destroy(); } + void assign ( none_t const& ) { destroy(); } #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT template void assign_expr ( Expr const& expr, Expr const* tag ) { - destroy(); - construct(expr,tag); + if (is_initialized()) + assign_expr_to_initialized(expr,tag); + else construct(expr,tag); } #endif @@ -244,7 +252,6 @@ class optional_base : public optional_tag void reset() { destroy(); } // 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 ) { assign(val); } // Returns a pointer to the value if this is initialized, otherwise, @@ -281,6 +288,21 @@ class optional_base : public optional_tag factory.apply(m_storage.address()) ; m_initialized = true ; } + + template + void assign_expr_to_initialized ( Expr const& factory, in_place_factory_base const* tag ) + { + destroy(); + construct(factory,tag); + } + + // Constructs in-place using the given typed factory + template + void assign_expr_to_initialized ( Expr const& factory, typed_in_place_factory_base const* tag ) + { + destroy(); + construct(factory,tag); + } #endif // Constructs using any expression implicitely convertible to the single argument @@ -294,6 +316,16 @@ class optional_base : public optional_tag m_initialized = true ; } + // Assigns using a form any expression implicitely convertible to the single argument + // of a T's assignment operator. + // Converting assignments of optional from optional uses this function with + // 'Expr' being of type 'U' and relying on a converting assignment of T from U. + template + void assign_expr_to_initialized ( Expr const& expr, void const* ) + { + assign_value(expr, is_reference_predicate()); + } + #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION // BCB5.64 (and probably lower versions) workaround. // The in-place factories are supported by means of catch-all constructors @@ -321,11 +353,14 @@ class optional_base : public optional_tag } #endif + void assign_value ( argument_type val, is_not_reference_tag ) { get_impl() = val; } + void assign_value ( argument_type val, is_reference_tag ) { construct(val); } + void destroy() - { - if ( m_initialized ) - destroy_impl(is_reference_predicate()) ; - } + { + if ( m_initialized ) + destroy_impl(is_reference_predicate()) ; + } unspecified_bool_type safe_bool() const { return m_initialized ? &this_type::is_initialized : 0 ; } @@ -391,7 +426,7 @@ class optional : public optional_detail::optional_base // Creates an optional uninitialized. // No-throw - optional( detail::none_t const& none_ ) : base(none_) {} + optional( none_t const& none_ ) : base(none_) {} // Creates an optional initialized with 'val'. // Can throw if T::T(T const&) does @@ -453,14 +488,7 @@ class optional : public optional_detail::optional_base template optional& operator= ( optional const& rhs ) { - this->destroy(); // no-throw - - if ( rhs.is_initialized() ) - { - // An exception can be thrown here. - // It it happens, THIS will be left uninitialized. - this->assign(rhs.get()); - } + this->assign(rhs.get()); return *this ; } #endif @@ -485,7 +513,7 @@ class optional : public optional_detail::optional_base // Assigns from a "none" // Which destroys the current value, if any, leaving this UNINITIALIZED // No-throw (assuming T::~T() doesn't) - optional& operator= ( detail::none_t const& none_ ) + optional& operator= ( none_t const& none_ ) { this->assign( none_ ) ; return *this ; @@ -607,62 +635,62 @@ bool operator >= ( optional const& x, optional const& y ) template inline -bool operator == ( optional const& x, detail::none_t const& ) +bool operator == ( optional const& x, none_t const& ) { return equal_pointees(x, optional() ); } template inline -bool operator < ( optional const& x, detail::none_t const& ) +bool operator < ( optional const& x, none_t const& ) { return less_pointees(x,optional() ); } template inline -bool operator != ( optional const& x, detail::none_t const& y ) +bool operator != ( optional const& x, none_t const& y ) { return !( x == y ) ; } template inline -bool operator > ( optional const& x, detail::none_t const& y ) +bool operator > ( optional const& x, none_t const& y ) { return y < x ; } template inline -bool operator <= ( optional const& x, detail::none_t const& y ) +bool operator <= ( optional const& x, none_t const& y ) { return !( y < x ) ; } template inline -bool operator >= ( optional const& x, detail::none_t const& y ) +bool operator >= ( optional const& x, none_t const& y ) { return !( x < y ) ; } template inline -bool operator == ( detail::none_t const& x, optional const& y ) +bool operator == ( none_t const& x, optional const& y ) { return equal_pointees(optional() ,y); } template inline -bool operator < ( detail::none_t const& x, optional const& y ) +bool operator < ( none_t const& x, optional const& y ) { return less_pointees(optional() ,y); } template inline -bool operator != ( detail::none_t const& x, optional const& y ) +bool operator != ( none_t const& x, optional const& y ) { return !( x == y ) ; } template inline -bool operator > ( detail::none_t const& x, optional const& y ) +bool operator > ( none_t const& x, optional const& y ) { return y < x ; } template inline -bool operator <= ( detail::none_t const& x, optional const& y ) +bool operator <= ( none_t const& x, optional const& y ) { return !( y < x ) ; } template inline -bool operator >= ( detail::none_t const& x, optional const& y ) +bool operator >= ( none_t const& x, optional const& y ) { return !( x < y ) ; } // @@ -679,8 +707,9 @@ namespace optional_detail { #endif // optional's swap: -// If both are initialized, calls swap(T&, T&), with whatever exception guarantess are given there. -// If only one is initialized, calls I.reset() and U.reset(*I), with the Basic Guarantee +// 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 @@ -688,12 +717,12 @@ void optional_swap ( optional& x, optional& y ) { if ( !x && !!y ) { - x.reset(*y); // Basic guarantee. + x.reset(*y); y.reset(); } else if ( !!x && !y ) { - y.reset(*x); // Basic guarantee. + y.reset(*x); x.reset(); } else if ( !!x && !!y ) diff --git a/test/optional_test.cpp b/test/optional_test.cpp index d42f733..9f60859 100644 --- a/test/optional_test.cpp +++ b/test/optional_test.cpp @@ -70,7 +70,7 @@ void test_basics( T const* ) // Implicit construction // The first parameter is implicitely converted to optional(a); test_implicit_construction(a,a,z); - + // Direct initialization. // 'oa' state is Initialized with 'a' // T::T( T const& x ) is used. @@ -85,7 +85,7 @@ void test_basics( T const* ) optional ob ; // Value-Assignment upon Uninitialized optional. - // T::T ( T const& x ) is used. + // T::T( T const& x ) is used. set_pending_copy( ARG(T) ) ; ob = a ; check_is_not_pending_copy( ARG(T) ) ; @@ -93,12 +93,14 @@ void test_basics( T const* ) check_value(ob,a,z); // Value-Assignment upon Initialized optional. - // T::T ( T const& x ) is used - set_pending_dtor( ARG(T) ) ; - set_pending_copy( ARG(T) ) ; + // T::operator=( T const& x ) is used + set_pending_assign( ARG(T) ) ; + set_pending_copy ( ARG(T) ) ; + set_pending_dtor ( ARG(T) ) ; ob = b ; - check_is_not_pending_dtor( ARG(T) ) ; - check_is_not_pending_copy( ARG(T) ) ; + check_is_not_pending_assign( ARG(T) ) ; + check_is_pending_copy ( ARG(T) ) ; + check_is_pending_dtor ( ARG(T) ) ; check_initialized(ob); check_value(ob,b,z); @@ -111,13 +113,14 @@ void test_basics( T const* ) 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) ) ; + // T::operator= ( T const& x ) is used to copy new value. + set_pending_assign( ARG(T) ) ; + set_pending_copy ( ARG(T) ) ; + set_pending_dtor ( ARG(T) ) ; oa = ob ; - check_is_not_pending_dtor( ARG(T) ) ; - check_is_not_pending_copy( ARG(T) ) ; + check_is_not_pending_assign( ARG(T) ) ; + check_is_pending_copy ( ARG(T) ) ; + check_is_pending_dtor ( ARG(T) ) ; check_initialized(oa); check_value(oa,b,z); @@ -161,7 +164,7 @@ template void test_direct_value_manip( T const* ) { TRACE( std::endl << BOOST_CURRENT_FUNCTION ); - + T x(3); optional const c_opt0(x) ; @@ -169,7 +172,7 @@ void test_direct_value_manip( T const* ) BOOST_CHECK( c_opt0.get().V() == x.V() ) ; BOOST_CHECK( opt0.get().V() == x.V() ) ; - + BOOST_CHECK( c_opt0->V() == x.V() ) ; BOOST_CHECK( opt0->V() == x.V() ) ; @@ -212,7 +215,7 @@ void test_uninitialized_access( T const* ) } catch (...) {} BOOST_CHECK(!passed); - + passed = false ; try { @@ -282,6 +285,9 @@ void test_throwing_direct_init( T const* ) BOOST_CHECK(!passed); check_is_not_pending_copy( ARG(T) ); check_instance_count(count, ARG(T) ); + + reset_throw_on_copy( ARG(T) ) ; + } // @@ -317,6 +323,8 @@ void test_throwing_val_assign_on_uninitialized( T const* ) check_is_not_pending_copy( ARG(T) ); check_instance_count(count, ARG(T) ); check_uninitialized(opt); + + reset_throw_on_copy( ARG(T) ) ; } // @@ -330,11 +338,10 @@ void test_throwing_val_assign_on_initialized( T const* ) T z(0); T a(8); T b(9); + T x(-1); int count = get_instance_count( ARG(T) ) ; - reset_throw_on_copy( ARG(T) ) ; - optional opt ( b ) ; ++ count ; @@ -342,16 +349,16 @@ void test_throwing_val_assign_on_initialized( T const* ) check_value(opt,b,z); - set_throw_on_copy( ARG(T) ) ; + set_throw_on_assign( 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) ) ; + // Attempt to assign 'a' and throw. + // opt is kept initialized but its value not neccesarily fully assigned + // (in this test, incompletely assigned is flaged with the value -1 being set) + set_pending_assign( ARG(T) ) ; opt.reset ( a ) ; passed = true ; } @@ -359,12 +366,12 @@ void test_throwing_val_assign_on_initialized( T const* ) BOOST_CHECK(!passed); - -- count ; - - check_is_not_pending_dtor( ARG(T) ); - check_is_not_pending_copy( ARG(T) ); + check_is_not_pending_assign( ARG(T) ); check_instance_count(count, ARG(T) ); - check_uninitialized(opt); + check_initialized(opt); + check_value(opt,x,z); + + reset_throw_on_assign ( ARG(T) ) ; } // @@ -378,8 +385,6 @@ void test_throwing_copy_initialization( T const* ) T z(0); T a(10); - reset_throw_on_copy( ARG(T) ) ; - optional opt (a); int count = get_instance_count( ARG(T) ) ; @@ -406,6 +411,8 @@ void test_throwing_copy_initialization( T const* ) // Nothing should have happened to the source optional. check_initialized(opt); check_value(opt,a,z); + + reset_throw_on_copy( ARG(T) ) ; } // @@ -420,8 +427,6 @@ void test_throwing_assign_to_uninitialized( T const* ) T z(0); T a(11); - reset_throw_on_copy( ARG(T) ) ; - optional opt0 ; optional opt1(a) ; @@ -446,6 +451,8 @@ void test_throwing_assign_to_uninitialized( T const* ) check_is_not_pending_copy( ARG(T) ); check_instance_count(count, ARG(T) ); check_uninitialized(opt0); + + reset_throw_on_copy( ARG(T) ) ; } // @@ -460,24 +467,23 @@ void test_throwing_assign_to_initialized( T const* ) T z(0); T a(12); T b(13); - - reset_throw_on_copy( ARG(T) ) ; + T x(-1); optional opt0(a) ; optional opt1(b) ; int count = get_instance_count( ARG(T) ) ; - set_throw_on_copy( ARG(T) ) ; + set_throw_on_assign( ARG(T) ) ; bool passed = false ; try { // This should: // Attempt to copy construct 'opt1.value()' into opt0 and throw. - // opt0 should be left unmodified or uninitialized - set_pending_dtor( ARG(T) ) ; - set_pending_copy( ARG(T) ) ; + // opt0 is kept initialized but its value not neccesarily fully assigned + // (in this test, incompletely assigned is flaged with the value -1 being set) + set_pending_assign( ARG(T) ) ; opt0 = opt1 ; passed = true ; } @@ -486,11 +492,12 @@ void test_throwing_assign_to_initialized( T const* ) BOOST_CHECK(!passed); // opt0 was left uninitialized - -- count ; - check_is_not_pending_dtor( ARG(T) ); - check_is_not_pending_copy( ARG(T) ); + check_is_not_pending_assign( ARG(T) ); check_instance_count(count, ARG(T) ); - check_uninitialized(opt0); + check_initialized(opt0); + check_value(opt0,x,z); + + reset_throw_on_assign( ARG(T) ) ; } // @@ -500,13 +507,11 @@ template void test_no_throwing_swap( T const* ) { TRACE( std::endl << BOOST_CURRENT_FUNCTION ); - + T z(0); T a(14); T b(15); - reset_throw_on_copy( ARG(T) ) ; - optional def0 ; optional def1 ; optional opt0(a) ; @@ -541,16 +546,15 @@ template void test_throwing_swap( T const* ) { TRACE( std::endl << BOOST_CURRENT_FUNCTION ); - + T a(16); T b(17); - - reset_throw_on_copy( ARG(T) ) ; + T x(-1); optional opt0(a) ; optional opt1(b) ; - set_throw_on_copy( ARG(T) ) ; + set_throw_on_assign( ARG(T) ) ; // // Case 1: Both Initialized. @@ -567,14 +571,18 @@ void test_throwing_swap( T const* ) 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 ) ) ) ) ) ; + // optional's swap doesn't affect the initialized states of the arguments. Therefore, + // the following must hold: + check_initialized(opt0); + check_initialized(opt1); + check_value(opt0,x,a); + check_value(opt1,b,x); + // // Case 2: Only one Initialized. // - reset_throw_on_copy( ARG(T) ) ; + reset_throw_on_assign( ARG(T) ) ; opt0.reset(); opt1.reset(a); @@ -585,7 +593,7 @@ void test_throwing_swap( T const* ) try { // This should attempt to swap optionals and fail at opt0.reset(*opt1) - // opt0 should be left uninitialized and opt1 unchanged. + // Both opt0 and op1 are left unchanged (unswaped) swap(opt0,opt1); passed = true ; @@ -596,7 +604,9 @@ void test_throwing_swap( T const* ) check_uninitialized(opt0); check_initialized(opt1); - check_value(opt1,a,b); + check_value(opt1,a,x); + + reset_throw_on_copy( ARG(T) ) ; } // @@ -606,9 +616,7 @@ template void test_relops( T const* ) { TRACE( std::endl << BOOST_CURRENT_FUNCTION ); - - reset_throw_on_copy( ARG(T) ) ; - + T v0(18); T v1(19); T v2(19); @@ -627,11 +635,11 @@ void test_relops( T const* ) // Check when both are uininitalized. BOOST_CHECK ( def0 == def1 ) ; // both uninitialized compare equal - BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized + BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized BOOST_CHECK ( !(def0 > def1) ) ; // uninitialized is never greater than uninitialized BOOST_CHECK ( !(def0 != def1) ) ; - BOOST_CHECK ( def0 <= def1 ) ; - BOOST_CHECK ( def0 >= def1 ) ; + BOOST_CHECK ( def0 <= def1 ) ; + BOOST_CHECK ( def0 >= def1 ) ; // Check when only lhs is uninitialized. BOOST_CHECK ( def0 != opt0 ) ; // uninitialized is never equal to initialized @@ -664,7 +672,7 @@ void test_none( T const* ) TRACE( std::endl << BOOST_CURRENT_FUNCTION ); using boost::none ; - + optional def0 ; optional def1(none) ; optional non_def( T(1234) ) ; @@ -682,7 +690,7 @@ void test_none( T const* ) void test_with_builtin_types() { TRACE( std::endl << BOOST_CURRENT_FUNCTION ); - + test_basics( ARG(double) ); test_uninitialized_access( ARG(double) ); test_no_throwing_swap( ARG(double) ); @@ -763,7 +771,7 @@ void test_conversions1() optional opt3 ; opt3 = opt2 ; BOOST_CHECK(*opt3 == d); -#endif +#endif } void test_conversions2() diff --git a/test/optional_test_common.cpp b/test/optional_test_common.cpp index fffe6fd..385651e 100644 --- a/test/optional_test_common.cpp +++ b/test/optional_test_common.cpp @@ -93,10 +93,22 @@ class X X& operator= ( X const& rhs ) { - v = rhs.v ; + pending_assign = false ; - TRACE ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ; + if ( throw_on_assign ) + { + TRACE ( "throwing exception in X's assignment" ) ; + v = -1 ; + + throw 0 ; + } + else + { + v = rhs.v ; + + TRACE ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ; + } return *this ; } @@ -115,7 +127,9 @@ class X static int count ; static bool pending_copy ; static bool pending_dtor ; + static bool pending_assign ; static bool throw_on_copy ; + static bool throw_on_assign ; private : @@ -127,32 +141,44 @@ class X } ; -int X::count = 0 ; -bool X::pending_copy = false ; -bool X::pending_dtor = false ; -bool X::throw_on_copy = false ; +int X::count = 0 ; +bool X::pending_copy = false ; +bool X::pending_dtor = false ; +bool X::pending_assign = false ; +bool X::throw_on_copy = false ; +bool X::throw_on_assign = 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 ( X const* x ) { X::pending_copy = true ; } +inline void set_pending_dtor ( X const* x ) { X::pending_dtor = true ; } +inline void set_pending_assign ( X const* x ) { X::pending_assign = true ; } +inline void set_throw_on_copy ( X const* x ) { X::throw_on_copy = true ; } +inline void set_throw_on_assign ( X const* x ) { X::throw_on_assign = true ; } +inline void reset_throw_on_copy ( X const* x ) { X::throw_on_copy = false ; } +inline void reset_throw_on_assign ( X const* x ) { X::throw_on_assign = 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_pending_assign ( X const* x ) { BOOST_CHECK( X::pending_assign ) ; } +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_is_not_pending_assign( X const* x ) { BOOST_CHECK( !X::pending_assign ) ; } +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 ; } +inline void set_pending_copy (...) {} +inline void set_pending_dtor (...) {} +inline void set_pending_assign (...) {} +inline void set_throw_on_copy (...) {} +inline void set_throw_on_assign (...) {} +inline void reset_throw_on_copy (...) {} +inline void reset_throw_on_assign (...) {} +inline void check_is_pending_copy (...) {} +inline void check_is_pending_dtor (...) {} +inline void check_is_pending_assign (...) {} +inline void check_is_not_pending_copy (...) {} +inline void check_is_not_pending_dtor (...) {} +inline void check_is_not_pending_assign(...) {} +inline void check_instance_count (...) {} +inline int get_instance_count (...) { return 0 ; } template @@ -160,7 +186,7 @@ inline void check_uninitialized_const ( optional const& opt ) { #ifndef BOOST_OPTIONAL_NO_NULL_COMPARE BOOST_CHECK( opt == 0 ) ; -#endif +#endif BOOST_CHECK( !opt ) ; BOOST_CHECK( !get_pointer(opt) ) ; BOOST_CHECK( !opt.get_ptr() ) ; diff --git a/test/optional_test_ref.cpp b/test/optional_test_ref.cpp index 4cf2506..1ccaeb4 100644 --- a/test/optional_test_ref.cpp +++ b/test/optional_test_ref.cpp @@ -32,7 +32,7 @@ inline void check_ref_uninitialized_const ( optional const& opt ) { #ifndef BOOST_OPTIONAL_NO_NULL_COMPARE BOOST_CHECK( opt == 0 ) ; -#endif +#endif BOOST_CHECK( !opt ) ; } template @@ -104,9 +104,16 @@ void test_basics( T const* ) T z(0); + T original_a(1); + T a(1); + T b(2); + + T c(10); + T& aref = a ; + T& bref = b ; // Default construction. // 'def' state is Uninitialized. @@ -115,13 +122,16 @@ void test_basics( T const* ) check_ref_uninitialized(def); // Direct initialization. - // 'oa' state is Initialized with 'a' + // 'oa' state is Initialized and binds to 'a' // T::T( T const& x ) is NOT used becasue the optional holds a reference. set_pending_copy( ARG(T) ) ; optional oa ( aref ) ; check_is_pending_copy( ARG(T) ); check_ref_initialized(oa); check_ref_value(oa,a,z); + *oa = b ; // changes the value of 'a' through the reference + BOOST_CHECK( a == b ) ; + // Copy initialization. // T::T ( T const& x ) is NOT used becasue the optional holds a reference. @@ -130,27 +140,32 @@ void test_basics( T const* ) check_is_pending_copy( ARG(T) ) ; check_ref_initialized_const(oa2); check_ref_value_const(oa2,a,z); + *oa2 = original_a ; // restores the value of 'a' through the reference + BOOST_CHECK( a == original_a ) ; - T b(2); optional ob ; // Value-Assignment upon Uninitialized optional. // T::T ( T const& x ) is NOT used becasue the optional holds a reference. set_pending_copy( ARG(T) ) ; - ob = a ; + ob = a ; // Binds ob to a temporary non-const refererence to 'a' check_is_pending_copy( ARG(T) ) ; check_ref_initialized(ob); check_ref_value(ob,a,z); + a = c; + check_ref_value(ob,a,z); // Value-Assignment upon Initialized optional. - // T::T ( T const& x ) is NOT used becasue the optional holds a reference. - set_pending_dtor( ARG(T) ) ; - set_pending_copy( ARG(T) ) ; - ob = b ; - check_is_pending_dtor( ARG(T) ) ; - check_is_pending_copy( ARG(T) ) ; + // T::operator= ( T const& x ) is used. + set_pending_assign( ARG(T) ) ; + ob = b ; // Rebinds 'ob' to 'b' (without changing 'a') + check_is_pending_assign( ARG(T) ) ; check_ref_initialized(ob); check_ref_value(ob,b,z); + BOOST_CHECK(a == c); // From a=c in previous test + b = c; + check_ref_value(ob,b,z); + // Assignment initialization. // T::T ( T const& x ) is NOT used becasue the optional holds a reference. @@ -162,14 +177,12 @@ void test_basics( T const* ) // Assignment - // T::~T() is used to destroy previous value in ob. - // T::T ( T const& x ) is NOT used becasue the optional holds a reference. - set_pending_dtor( ARG(T) ) ; - set_pending_copy( ARG(T) ) ; - oa = ob ; - check_is_pending_dtor( ARG(T) ) ; - check_is_pending_copy( ARG(T) ) ; + // T::operator=( T const& x ) is used. + set_pending_assign( ARG(T) ) ; + oa = ob ; // Rebinds 'a' to 'b' + check_is_pending_assign( ARG(T) ) ; check_ref_initialized(oa); + a = original_a ; check_ref_value(oa,b,z); // Uninitializing Assignment upon Initialized Optional @@ -190,6 +203,7 @@ void test_basics( T const* ) check_is_pending_copy( ARG(T) ) ; check_ref_uninitialized(oa); + // Deinitialization of Initialized Optional // T::~T() is NOT used becasue the optional holds a reference. set_pending_dtor( ARG(T) ) ; @@ -212,9 +226,9 @@ template void test_relops( T const* ) { TRACE( std::endl << BOOST_CURRENT_FUNCTION ); - + reset_throw_on_copy( ARG(T) ) ; - + T v0(18); T v1(19); T v2(19); @@ -233,11 +247,11 @@ void test_relops( T const* ) // Check when both are uininitalized. BOOST_CHECK ( def0 == def1 ) ; // both uninitialized compare equal - BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized - BOOST_CHECK ( !(def0 > def1) ) ; // uninitialized is never greater than uninitialized + BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized + BOOST_CHECK ( !(def0 > def1) ) ; // uninitialized is never greater than uninitialized BOOST_CHECK ( !(def0 != def1) ) ; - BOOST_CHECK ( def0 <= def1 ) ; - BOOST_CHECK ( def0 >= def1 ) ; + BOOST_CHECK ( def0 <= def1 ) ; + BOOST_CHECK ( def0 >= def1 ) ; // Check when only lhs is uninitialized. BOOST_CHECK ( def0 != opt0 ) ; // uninitialized is never equal to initialized @@ -254,7 +268,7 @@ void test_relops( T const* ) BOOST_CHECK ( opt0 > def0 ) ; BOOST_CHECK ( !(opt0 <= def0) ) ; BOOST_CHECK ( opt0 >= opt0 ) ; - + // If both are initialized, values are compared BOOST_CHECK ( opt0 != opt1 ) ; BOOST_CHECK ( opt1 == opt2 ) ; @@ -270,7 +284,7 @@ void test_none( T const* ) TRACE( std::endl << BOOST_CURRENT_FUNCTION ); using boost::none ; - + T a(1234); optional def0 ;