commit 6277c98514c9670775b559c8d0e72bafebf66fc8 Author: Fernando Cacciola Date: Wed Jan 22 17:54:11 2003 +0000 Initial versions [SVN r16995] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3e84d7c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,96 @@ +* text=auto !eol svneol=native#text/plain +*.gitattributes text svneol=native#text/plain + +# Scriptish formats +*.bat text svneol=native#text/plain +*.bsh text svneol=native#text/x-beanshell +*.cgi text svneol=native#text/plain +*.cmd text svneol=native#text/plain +*.js text svneol=native#text/javascript +*.php text svneol=native#text/x-php +*.pl text svneol=native#text/x-perl +*.pm text svneol=native#text/x-perl +*.py text svneol=native#text/x-python +*.sh eol=lf svneol=LF#text/x-sh +configure eol=lf svneol=LF#text/x-sh + +# Image formats +*.bmp binary svneol=unset#image/bmp +*.gif binary svneol=unset#image/gif +*.ico binary svneol=unset#image/ico +*.jpeg binary svneol=unset#image/jpeg +*.jpg binary svneol=unset#image/jpeg +*.png binary svneol=unset#image/png +*.tif binary svneol=unset#image/tiff +*.tiff binary svneol=unset#image/tiff +*.svg text svneol=native#image/svg%2Bxml + +# Data formats +*.pdf binary svneol=unset#application/pdf +*.avi binary svneol=unset#video/avi +*.doc binary svneol=unset#application/msword +*.dsp text svneol=crlf#text/plain +*.dsw text svneol=crlf#text/plain +*.eps binary svneol=unset#application/postscript +*.gz binary svneol=unset#application/gzip +*.mov binary svneol=unset#video/quicktime +*.mp3 binary svneol=unset#audio/mpeg +*.ppt binary svneol=unset#application/vnd.ms-powerpoint +*.ps binary svneol=unset#application/postscript +*.psd binary svneol=unset#application/photoshop +*.rdf binary svneol=unset#text/rdf +*.rss text svneol=unset#text/xml +*.rtf binary svneol=unset#text/rtf +*.sln text svneol=native#text/plain +*.swf binary svneol=unset#application/x-shockwave-flash +*.tgz binary svneol=unset#application/gzip +*.vcproj text svneol=native#text/xml +*.vcxproj text svneol=native#text/xml +*.vsprops text svneol=native#text/xml +*.wav binary svneol=unset#audio/wav +*.xls binary svneol=unset#application/vnd.ms-excel +*.zip binary svneol=unset#application/zip + +# Text formats +.htaccess text svneol=native#text/plain +*.bbk text svneol=native#text/xml +*.cmake text svneol=native#text/plain +*.css text svneol=native#text/css +*.dtd text svneol=native#text/xml +*.htm text svneol=native#text/html +*.html text svneol=native#text/html +*.ini text svneol=native#text/plain +*.log text svneol=native#text/plain +*.mak text svneol=native#text/plain +*.qbk text svneol=native#text/plain +*.rst text svneol=native#text/plain +*.sql text svneol=native#text/x-sql +*.txt text svneol=native#text/plain +*.xhtml text svneol=native#text/xhtml%2Bxml +*.xml text svneol=native#text/xml +*.xsd text svneol=native#text/xml +*.xsl text svneol=native#text/xml +*.xslt text svneol=native#text/xml +*.xul text svneol=native#text/xul +*.yml text svneol=native#text/plain +boost-no-inspect text svneol=native#text/plain +CHANGES text svneol=native#text/plain +COPYING text svneol=native#text/plain +INSTALL text svneol=native#text/plain +Jamfile text svneol=native#text/plain +Jamroot text svneol=native#text/plain +Jamfile.v2 text svneol=native#text/plain +Jamrules text svneol=native#text/plain +Makefile* text svneol=native#text/plain +README text svneol=native#text/plain +TODO text svneol=native#text/plain + +# Code formats +*.c text svneol=native#text/plain +*.cpp text svneol=native#text/plain +*.h text svneol=native#text/plain +*.hpp text svneol=native#text/plain +*.ipp text svneol=native#text/plain +*.tpp text svneol=native#text/plain +*.jam text svneol=native#text/plain +*.java text svneol=native#text/plain diff --git a/doc/optional.html b/doc/optional.html new file mode 100644 index 0000000..8ed169f --- /dev/null +++ b/doc/optional.html @@ -0,0 +1,982 @@ + + + + + + + +Header + + + +

Header <boost/optional.hpp>

+ +

Contents

+
+
Motivation
+
Development
+
Synopsis
+
Semantics
+
Examples
+
A note about optional<bool>
+
Exception Safety Guarantees
+
Type requirements
+
Implementation Notes
+
Dependencies and Portability
+
Acknowledgment
+
+ +
+ +

Motivation

+ +

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

+
(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.

+

A typical approach is to consider the existence of a valid return value as + a postcondition, so that if the function cannot compute the value to return, + it has either undefined behavior (and can use asssert in a debug build) + or uses a runtime check and throws an exception if the postcondition is violated. + This is a reasonable choice for example, for function (A), because the + lack of a proper return value is directly related to an invalid parameter (out + of domain argument), so it is appropriate to require the callee to supply only + parameters in a valid domain for execution to continue normally.

+

However, function (B), because of its asynchronous nature, does not fail just + because it can't find a value to return; so it is incorrect to consider + such a situation an error and assert or throw an exception. This function must + return, and somehow, must tell the callee that it is not returning a meaningful + value.

+

A similar situation occurs with function (C): it is conceptually an error to + ask a null-area polygon to return a point inside itself, but in many + applications, it is just impractical for performance reasons to treat this as + an error (because detecting that the polygon has no area might be too expensive + to be required to be tested previously), and either an arbitrary point (typically + at infinity) is returned, or some efficient way to tell the callee that there + is no such point is used.

+

There are various mechanisms to let functions communicate that the returned + value is not valid. One such mechanism, which is quite common since it has zero + or negligible overhead, is to use a special value which is reserved to communicate + this. Classical examples of such special values are EOF, string::npos, points + at infinity, etc...

+

When those values exist, i.e. the return type can hold all meaningful values + plus the signal value, this mechanism is quite appropriate and + well known. Unfortunately, there are cases when such values do not exist. In + these cases, the usual alternative is either to use a wider type, such as 'int' + in place of 'char'; or a compound type, such as std::pair<point,bool>. +

+

Returning a std::pair<T,bool>, thus attaching a boolean flag to the result + which indicates if the result is meaningful, has the advantage that can be turned + into a consistent idiom since the first element of the pair can be whatever + the function would conceptually return. For example, the last two functions + could have the following interface:

+
std::pair<char,bool> get_async_input();
+std::pair<point,bool> polygon::get_any_point_effectively_inside();
+

These functions use a consistent interface for dealing with possibly inexistent + results:

+
std::pair<point,bool> p = poly.get_any_point_effectively_inside();
+if ( p.second )
+  flood_fill(p.first);
+
+ +

However, not only is this quite a burden syntactically, it is also error + prone since the user can easily use the function result (first element of the + pair) without ever checking if it has a valid value.

+

Clearly, we need a better idiom.

+ +

Development

+ +

The model

+

In C++, we can declare an object (a variable) of type T, and we can give this variable + an initial value (through an initializer. (c.f. 8.5)).
+ When a declaration includes a non-empty inilializer (an initial value is given), it is said that + the object has been initialized.
+ If the declaration uses an empty initializer (no initial value is given), + 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 + 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 + no value at all and this situation can be tested at runtime. It is formally undefined behvaiour + 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. And given the formal treatment of initialization + states in optional objects, it is even possible to + reset an optional to uninitialized.

+

In C++ there is no formal notion of uninitialized objects, which + means that objects always have an initial value even if indeterminate.
+ As discussed on the previous section, this has a drawback because you need additional + information to tell if an object has been effectively initialized.
+ One of the typical ways in which this has been historically + dealt with is via a special value: EOF,npos,-1, etc... This is equivalent to adding + the special value to the set of possibles values of a given type. + On modern languages, this can be modeled with a discriminated + union of T and something else such as a trivial POD or enum. + Discriminated unions are often called variants. + A variant has a current type, which in our case is either T or something else. In C++, + such a variant would be typically implemented as a template class of the form: variant<T,nil_t> +

+

There is precedence for a discriminated union as a model for an optional + value: the Haskell Maybe builtin type constructor.

+

A discriminated union, which can be seen as a container which has an object of either + type T or something else, has exactly the semantics required for a wrapper of optional values:

+
  • deep-copy semantics: copies of the variant implies copies of the contained value.
  • +
  • deep-relational semantics: comparions between variants matches both current types and values
  • +
  • If the variant's current type is T, it is modeling an initialized optional.
  • +
  • If the variant's current type is not T, it is modeling an uninitialized optional.
  • +
  • Testing if the variant's current type is T models testing if the optional is initialized
  • +
  • Trying to extract a T from a variant when its current type is not T, models the undefined behaviour +of trying to access the value of an uninitialized optional
  • +

    Because of the way a discriminated union is used for this purpose, it only matters + whether its current type is T or not. We can put a layer on top of the variant hidding the other type + transforming a container of fixed size 1 into a variable size container which either has + a T or has nothing. Thus, the variant<T,nil_t> can be seen as if it were a variable-size + fixed-capacity stack-based container with the following optional-oriented interface:

    + + +
    // Uninitialized (internally, current type is nil_t)
    +optional<T>::optional();
    +
    +// Initialized with 'v' (internally, current type is T)
    +optional<T>::optional( T const& v ) ;
    +
    +// Back to uninitialized (current type is set to nil_t)
    +void optional<T>::reset();
    +
    +// Assigns 'v' whether previously initialized or not (current type is set to T)
    +void optional<T>::reset( T const& v ) ;
    +
    +// Returns 'true' if the optional is initialized, 'false' otherwise.
    +bool optional<T>::initialized() ;
    +
    +// If the optional is initialized (current type is T), returns a reference to its value.
    +// Otherwise (current type is nil_t), the result is undefined.
    +T const& optional<T>::ref() const ;
    +T&       optional<T>::ref() ;
    +
    +// If both are initialized, calls swap(T&,T&);
    +// If only one is initialized, calls reset(T const&) and reset().
    +// If both are uninitalized, do nothing.
    +void swap ( optional<T>& lhs, optional<T>& rhs ) ;
    +
    +// If both are initialized, compare values.
    +// If only one is initialized, they are not equal.
    +// If both are uninitalized, they are equal.
    +bool operator == ( optional<T> const& lhs, optional<T> const& rhs ) ;
    +bool operator != ( optional<T> const& lhs, optional<T> const& rhs ) ;
    +
    + +

    Pointers and optional objects

    +

    In C++, unlike many other languages, objects can be referenced indirectly + by means of a pointer (or a reference). Pointers have several nice features, + two of which are relevant to this development.

    +

    One is that a pointer has its own pointer value, which in effect + references the object being pointed to: the pointee. Consequently, + copies of pointers do not involve copies of pointees. This effect results in aliasing: + different pointers can refer to the same object. + The particular semantic that a copy of a pointer does not involve + a copy of the pointee is called shallow-copy, which is opossed to deep-copy were + a copy of a wrapper involves a copy of the wrapped object (as with optional<>)
    + Since this is the semantic followed by pointers (and references), shallow-copy + (and therefore aliasing) is implied in pointer semantics.

    +

    The other relevant feature of a pointer is that a pointer can have a null + pointer value. This is a special value which is used to indicate that the + pointer is not referring to any object at all. In other words, null pointer + values convey the notion of inexisting objects.

    +

    This meaning of the null pointer value allowed pointers to became a defacto standard + for handling optional objects because all you have to do to refer to a value which you + don't really have is to use a null pointer value of the appropriate type. + Pointers have been used for decades -from the days of C APIs to modern C++ libraries- + to refer to optional (that is, possibly inexisting) objects; particularly + as optional arguments to a function, but also quite often as optional data members.

    +

    The possible presence of a null pointer value makes the operations that access the + pointee's value possibly undefined, therefore, expressions which use dereference + and access operators, such as: ( *p = 2 ) and ( p->foo()), + implicitely convey the notion of optionality, and this information is tied to + the syntax of the expressions. That is, the presence of operators * and -> tell by + themselves -without any additional context-, that the expression will be undefined unless + the implied pointee actually exist.
    + Furthermore, the existence of the pointee can be tested by a comparison against + the null pointer value or via a conversion to bool, which allows expressions of + the form: if ( p != 0 ), or if ( p ) to be used to test for the existence of the pointee.

    +

    Such a defacto idiom for referring to optional objects can be formalized in the form of a +concept: the OptionalPointee concept.
    +This concept captures the syntatic usage of operatos *, -> and conversion to bool to convey +the notion of optionality.

    +

    However, pointers are good to refer to optional objects, but not particularly good +to handle the optional objects in all other respects, such as initializing or moving/copying +them. The problem resides in the shallow-copy of pointer semantics: if you need to + effectively move or copy the object, pointers alone are not enough. The problem + is that copies of pointers do not imply copies of pointees. For example, as + was discussed in the motivation, pointers alone cannot be used to return optional + objects from a function because the object must move outside from the function and + into the caller's context.
    + A solution to the shallow-copy problem that is often used is to resort to dynamic + allocation and use a smart pointer to automatically handle the details of this. + For example, if a function is to optionally return an object X, it can use shared_ptr<X> + as the return value. However, this requires dynamic allocation of X. If X is + a builtin or small POD, this technique is very poor in terms of required resources. + Optional objects are essentially values so it is very convenient to be able to use automatic + storage and deep-copy semantics to manipulate optional values just as we do with ordinary + values. Pointers do not have this semantics so are unapprorpiate for the initialization and + transport of optional values, yet are quite convenient for handling the access to the + possible undefined value because of the idiomatic aid present in the OptionalPointee + concept incarnated by pointers.
    + Therefore, the final solution which is presented in this library is to shape the + previously discussed optional -which is a value-based container- as a model + of the OptionalPointee concept. +

    +

    Optional<T> as a model of OptionalPointee

    +

    The optional<> template class presented with this library is a variation of the + sketch shown before (as a layer on top of a variant). It features deep-copy and + deep relational operators, but also models the OptionalPointee concept. + Instead of the member function 'initialized()' it has a safe conversion to bool, + and instead of the 'ref()' member function, it has operators*() and ->().
    + However, it is particularly important that optional<> objects are not mistaken by pointers, + they are not. optional<> does not model a pointer. + For instance, optional<> has not shallow-copy so does not alias: two different optionals + never refer to the same value (but my have equivalent values).
    + The difference between an optional<T> and a pointer must be kept in mind, particularly + because the semantics of relational operators are different: since optional<T> + is a value-wrapper, relational operators are deep: they compare optional values; + but relational operators for pointers are shallow: they do not compare pointee values.
    + As a result, you might be able to replace optional<T> by T* on some situations but + not always. Specifically, on generic code written for both, you cannot use relational + operators directly, and must use the template function + equal_pointees() instead. +


    + +

    Synopsis

    + +
    namespace boost {
    +
    +template<class T>
    +class optional
    +{
    +  public :
    +
    +    optional () ;
    +
    +    explicit optional ( T const& v ) ;
    +
    +    optional ( optional const& rhs ) ;
    +
    +    template<class U> explicit optional ( optional<U> const& rhs ) ;
    +
    +    optional& operator = ( optional const& rhs ) ;
    +
    +    template<class U> optional& operator = ( optional<U> const& rhs ) ;
    +
    +    T const* get() const ;
    +    T*       get() ;
    +
    +    T const* operator ->() const ;
    +    T*       operator ->() ;
    +
    +    T const& operator *() const ;
    +    T&       operator *() ;
    +
    +    void reset();
    +
    +    void reset ( T const& ) ;
    +
    +    operator unspecified-bool-type() const ;
    +
    +    bool operator!() const ;
    +
    +} ;
    +
    +template<class T> inline bool operator == ( optional<T> const& x, optional<T> const& y ) ;
    +
    +template<class T> inline bool operator != ( optional<T> const& x, optional<T> const& y ) ;
    +
    +template<class T> inline T* get_pointer ( optional<T> const& opt ) ;
    +
    +template<class T> inline void swap( optional<T>& x, optional<T>& y ) ;
    +
    +} // namespace boost
    +
    + +
    + +

    Semantics

    + +

    Note: the following section contains various assert() which are used only to + show the postconditions as sample code. + It is not implied that the type T must support each particular expression + but that if the expression is supported, the implied condition holds.

    +
    + +
    optional<T>::optional();
    +
    +

    Effect: Default-Constructs an optional.

    +

    Postconditions: *this is uninitialized.

    +

    Throws: Nothing.

    +

    Notes: T's default constructor is not called.

    +

    Example:

    +
    +
    optional<T> def ;
    +assert ( !def ) ;
    +
    +
    + +
    + +
    explicit optional<T>::optional( T const& v )
    +
    +

    Effect: Directly-Constructs an optional.

    +

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

    +

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

    +

    Notes: T::T( T const& ) is called.

    +

    Exception Safety: Exceptions can only be thrown during T::T( T const& ); +in that case, this constructor has no effect. +

    +

    Example:

    +
    +
    +T v;
    +optional<T> opt(v);
    +assert ( *opt == v ) ;
    +
    +
    +
    + +
    + +
    optional<T>::optional( optional const& rhs );
    +
    +

    Effect: Copy-Constructs an optional.

    +

    Postconditions: If rhs is initialized, *this is initialized +and its value is a copy of the value of rhs; else *this +is uninitialized.

    +

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

    +

    Notes: T::T( T const& ) is called if rhs is initialized.

    +

    Exception Safety: Exceptions can only be thrown during T::T( T const& ); +in that case, this constructor has no effect. +

    +

    Example:

    +
    +
    optional<T> uninit ;
    +assert (!uninit);
    +
    +optional<T> uinit2 ( uninit ) ;
    +assert ( uninit2 == uninit );
    +
    +optional<T> init( T(2) );
    +assert ( *init == T(2) ) ;
    +
    +optional<T> init2 ( init ) ;
    +assert ( init2 == init ) ;
    +
    + +
    +
    + +
    + +
    explicit template<U> optional<T>::optional( optional<U> const& rhs );
    +
    +

    Effect: Copy-Constructs an optional.

    +

    Postconditions: If rhs is initialized, *this is initialized + 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: T::T( U const& ) is called if rhs is initialized, which requires +a valid conversion from U to T. +

    +

    Exception Safety: Exceptions can only be thrown during T::T( U const& ); +in that case, this constructor has no effect. +

    +

    Example:

    +
    + +
    optional<double> x(123.4);
    +assert ( *x == 123.4 ) ;
    +
    +optional<int> y(x) ;
    +assert( *y == 123 ) ;
    +
    +
    +
    + +
    + +
    optional& optional<T>::operator= ( optional const& rhs ) ;
    +
    +

    Effect: Assigns another optional to an optional.

    +

    Postconditions: If rhs is initialized, *this is initialized +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. +

    +

    Example:

    +
    +
    +T v;
    +optional<T> opt(v);
    +optional<T> uninit ;
    +
    +opt = uninit ;
    +assert ( !opt ) ;
    +// previous value (copy of 'v') destroyed from within 'opt'.
    +
    +
    +
    +
    + +
    + +
    template<U> optional& optional<T>::operator= ( optional<U> const& rhs ) ;
    +
    +

    Effect: Assigns another convertible optional to an optional.

    +

    Postconditions: If rhs is initialized, *this is initialized +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. +

    +

    Example:

    +
    +
    +T v;
    +optional<T> opt0(v);
    +optional<U> opt1;
    +
    +opt1 = opt0 ;
    +assert ( *opt1 == static_cast<U>(v) ) ;
    +
    +
    +
    + +
    +
    void optional<T>::reset( T const& v ) ;
    +
    +

    Effect: Resets the current value.

    +

    Postconditions: *this is initialized and its value is +a copy of 'v'. +

    +

    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. +

    +

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

    +

    Example:

    +
    +
    optional<T> opt ( some_T ) ;
    +assert( *opt == some_T );
    +opt.reset ( some_other_T ) ;
    +assert( *opt == some_other_T );
    +
    +
    +
    + +
    +
    void optional<T>::reset() ;
    +
    +

    Effect: Destroys the current value.

    +

    Postconditions: *this is uninitialized.

    +

    Throws: Nothing.

    +

    Notes: T::~T() is called.

    +

    Example:

    +
    +
    optional<T> opt ( some_T ) ;
    +assert( *opt == some_T );
    +opt.reset();
    +assert( !opt );
    +
    +
    +
    + +
    + +
    +T const* optional<T>::get() const ;
    +T*       optional<T>::get() ;
    +
    +inline T const* get_pointer ( optional<T> const& ) ;
    +inline T*       get_pointer ( optional<T>&) ;
    +
    +
    +

    Returns: If *this is initialized, a pointer to the contained +value; else 0 (null). +

    +

    Throws: Nothing.

    +

    Notes: The contained value is permanently stored within *this, so +you should not hold nor delete this pointer +

    +

    Example:

    +
    +
    +T v;
    +optional<T> opt(v);
    +optional<T> const copt(v);
    +T* p = opt.get() ;
    +T const* cp = copt.get();
    +assert ( p == get_pointer(opt) );
    +assert ( cp == get_pointer(copt) ) ;
    +
    +
    +
    + + +
    + + +
    +T const* optional<T>::operator ->() const ;
    +T*       optional<T>::operator ->()       ;
    +
    +
    +

    Requirements: *this is initialized.

    +

    Returns: A pointer to the contained value.

    +

    Throws: Nothing.

    +

    Notes: The requirement is asserted via BOOST_ASSERT().

    +

    Example:

    +
    +
    +struct X { int mdata ; } ;
    +X x ;
    +optional<X> opt (x);
    +opt->mdata = 2 ;
    +
    +
    +
    + + +
    + + +
    T const& optional<T>::operator*() const ;
    +T&       optional<T>::operator*();
    +
    +

    Requirements: *this is initialized

    +

    Returns: A reference to the contained value

    +

    Throws: Nothing.

    +

    Notes: The requirement is asserted via BOOST_ASSERT().

    +

    Example:

    +
    +
    T v ;
    +optional<T> opt ( v );
    +T const& u = *opt;
    +assert ( u == v ) ;
    +T w ;
    +*opt = w ;
    +assert ( *opt == w ) ;
    +
    +
    +
    + +
    +
    optional<T>::operator unspecified-bool-type() const ;
    +
    +

    Returns: An unspecified value which if used on a boolean context is equivalent to (get() != 0)

    +

    Throws: Nothing.

    +
    +
    optional<T> def ;
    +assert ( def == 0 );
    +optional<T> opt ( v ) ;
    +assert ( opt );
    +assert ( opt != 0 );
    +
    +
    +
    + +
    + + +
     bool optional<T>::operator!() ;
    +
    +

    Returns: If *this is uninitialized, true; else false.

    +

    Throws: Nothing.

    +

    Notes: This operator is provided for those compilers which can't use +the unspecified-bool-type operator in certain boolean contexts. +

    +

    Example:

    +
    +
    optional<T> opt ;
    +assert ( !opt );
    +*opt = some_T ;
    +
    +// Notice the "double-bang" idiom here.
    +assert ( !!opt ) ;
    +
    +
    +
    + +
    + + +
    bool operator == ( optional<T> const& x, optional<T> const& y );
    +
    +

    Returns: If both x and y are initialied, (*x == *y). +If only x or y is initialized, false. If both are uninitialized, true. +

    +

    Throws: Nothing.

    +

    Notes: Pointers have shallow relational operators while optional has +deep relational operators. Do not use operator == directly in generic code +which expect to be given either an optional<T> or a pointer; +use equal_pointees() instead +

    +

    Example:

    +
    +
    +T x(12);
    +T y(12);
    +T z(21);
    +optional<T> def0 ;
    +optional<T> def1 ;
    +optional<T> optX(x);
    +optional<T> optY(y);
    +optional<T> optZ(z);
    +
    +// Identity always hold
    +assert ( def0 == def0 );
    +assert ( optX == optX );
    +
    +// Both uninitialized compare equal
    +assert ( def0 == def1 );
    +
    +// Only one initialized compare unequal.
    +assert ( def0 != optX );
    +
    +// Both initialized compare as (*lhs == *rhs)
    +assert ( optX == optY ) ;
    +assert ( optX != optZ ) ;
    +
    +
    +
    + +
    +
    bool operator != ( optional<T> const& x, optional<T> const& y );
    +
    +
    +

    Returns: !( x == y );

    +

    Throws: Nothing.

    +
    + +
    + +
    void swap ( optional<T>& x, optional<T>& y );
    + +
    +

    Effect: If both x and y are initialized, calls swap(*x,*y) +using std::swap.
    +If only one is initialized, say x, calls: y.reset(*x); x.reset();
    +If none is initialized, does nothing. +

    +

    Postconditions: The states of x and y interchanged.

    +

    Throws: If both are initialized, whatever swap(T&,T&) throws. +If only one is initialized, whatever T::T ( T const& ) throws. +

    +

    Notes: If both are initialized, swap(T&,T&) is used unqualified +but with std::swap introduced in scope.
    +If only one is initialized, T::~T() and T::T( T const& ) is called. +

    +

    Exception Safety: If both are initialized, this operation has the exception +safety guarantees of swap(T&,T&).
    +If only one is initialized, it has the same basic guarantee as optional<T>::reset( T const& ). +

    +

    Example:

    +
    +
    +T x(12);
    +T y(21);
    +optional<T> def0 ;
    +optional<T> def1 ;
    +optional<T> optX(x);
    +optional<T> optY(y);
    +
    +boost::swap(def0,def1); // no-op
    +
    +boost::swap(def0,optX);
    +assert ( *def0 == x );
    +assert ( !optX );
    +
    +boost::swap(def0,optX); // Get back to original values
    +
    +boost::swap(optX,optY);
    +assert ( *optX == y );
    +assert ( *optY == x );
    +
    +
    +
    +
    +
    + +

    Examples

    + +

    Optional return values

    +
    optional<char> get_async_input()
    +{
    +  if ( !queue.empty() )
    +       return optional<char>(queue.top());
    +  else return optional<char>(); // uninitialized
    +}
    +
    +void recieve_async_message()
    +{
    +  optional<char> rcv ;
    +  // The safe boolean conversion from 'rcv' is used here.
    +  while ( (rcv = get_async_input()) && !timeout() )
    +    output(*rcv);
    +}
    +
    + +

    Optional local variables

    +
    optional<string> name ;
    +if ( database.open() )
    +{
    +  name.reset ( database.lookup(employer_name) ) ;
    +}
    +else
    +{
    +  if ( can_ask_user )
    +    name.reset ( user.ask(employer_name) ) ;
    +}
    +
    +if ( name )
    +     print(*name);
    +else print("employer's name not found!");
    +
    + +

    Optional data members

    +
    class figure
    +{
    +  public:
    +
    +    figure()
    +    {
    +      // data member 'm_clipping_rect' is uninitialized at this point.
    +    }
    +
    +    void clip_in_rect ( rect const& rect )
    +      {
    +         ....
    +         m_clipping_rect.reset ( rect ) ; // initialized here.
    +      }
    +
    +    void draw ( canvas& cvs )
    +      {
    +        if ( m_clipping_rect )
    +          do_clipping(*m_clipping_rect);
    +
    +        cvs.drawXXX(..);
    +      }
    +
    +    // this can return NULL.
    +    rect const* get_clipping_rect() { return get_pointer(m_clipping_rect); }
    +
    +  private :
    +
    +    optional<rect> m_clipping_rect ;
    +
    +};
    +
    +

    Bypassing expensive unnecesary default construction

    +
    class ExpensiveCtor { ... } ;
    +class Fred
    +{
    +  Fred() : mLargeVector(10000) {}
    +
    +  std::vector< optional<ExpensiveCtor> > mLargeVector ;
    +} ;
    +
    + +
    + +

    A note about optional<bool>

    +

    optional<bool> should be used with special caution and consideration.

    +

    First, it is functionally similar to a tristate boolean (false,maybe,true) +-such as boost::tribool-, except that in a tristate boolean, the maybe state +represents a valid value, unlike the corresponding state +of an uninitialized optional<bool>.
    +It should be carefully considered if an optional bool instead of a tribool is really needed

    +

    Second, optional<> provides and implicit conversion to bool. This conversion +refers to the initialization state and not to the contained value.
    +Using optional<bool> can lead to subtle errors due to the implicit bool conversion:

    +
    +void foo ( bool v ) ;
    +void bar()
    +{
    +  optional<bool> v = try();
    +
    +  // The following intended to pass the value of 'v' to foo():
    +  foo(v);
    +  // But instead, the initialization state is passed
    +  // due to a typo: it should have been foo(*v).
    +}
    +
    +

    The only implicit conversion is to bool, and it is safe in the sense that typical +integral promotions don't apply (i.e. if foo() takes an 'int' instead, it won't compile). +


    + +

    Exception Safety Guarantees

    +

    Assignment and Reset:

    +

    Because of the current implementation (see Implementation Notes), + optional<T>::operator=( optional<T> const& ) +and 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())

    +

    optional<T>::reset() provides the no-throw guarantee (assuming a no-throw T::~T())

    +

    However, since optional<> itself doesn't throw any exceptions, +the only source for exceptions here is T's copy constructor, so if you know the exception guarantees +for T::T ( T const& ), you know that optional's assignment and reset has the same guarantees.

    +
    //
    +// Case 1: Exception thrown during assignment.
    +//
    +T v0(123);
    +optional<T> opt0(v0);
    +try
    +{
    +  T v1(456);
    +  optional<T> opt1(v1);
    +  opt0 = opt1 ;
    +
    +  // If no exception was thrown, assignment succeeded.
    +  assert( *opt0 == v1 ) ;
    +}
    +catch(...)
    +{
    +  // If any exception was thrown, 'opt0' is reset to uninitialized.
    +  assert( !opt0 ) ;
    +}
    +
    +//
    +// Case 2: Exception thrown during reset(v)
    +//
    +T v0(123);
    +optional<T> opt(v0);
    +try
    +{
    +  T v1(456);
    +  opt.reset ( v1 ) ;
    +
    +  // If no exception was thrown, reset succeeded.
    +  assert( *opt == v1 ) ;
    +}
    +catch(...)
    +{
    +  // If any exception was thrown, 'opt' is reset to uninitialized.
    +  assert( !opt ) ;
    +}
    +
    +

    Swap:

    +

    void swap( optional<T>&, optional<T>& ) +has the same exception guarantee as swap(T&,T&) when both optionals are initialized.
    +If only one of the optionals is initialized, it gives the same +basic exception guarantee as optional<T>::reset( T const& ) +(since optional<T>::reset() doesn't throw).
    +If none of the optionals is initialized, it has no-throw guarantee since it is a no-op. +

    + +
    + +

    Type requirements

    +

    T must be Copy Constructible +and have a no-throw destructor.
    +T is not required to be +Default Constructible +

    + +
    + +

    Implementation Notes

    +

    optional<T> is currently implemented + using a custom aligned storage facility built from alignment_of and + type_with_alignment (both from Type Traits). + It uses a separate boolean flag to indicate the initialization state.
    + Placement new with T's copy constructor and T's destructor + are explicitely used to initialize,copy and destroy optional values.
    + As a result, T's default constructor is effectively by-passed, but the exception + guarantess are basic.
    + It is planned to replace the current implementation with another with + stronger exception safety, such as a future boost::variant. +

    + +
    + +

    Dependencies and Portability

    + +

    The implementation uses type_traits/alignement_of.hpp +and type_traits/type_with_alignement.hpp

    +

    It has been tested on bcc5.5.1, vc6.0 and gcc2.95.2

    + +
    + +

    Acknowledgments

    +

    Pre-formal review:

    +
    +

    Peter Dimov suggested the name 'optional', and was the first to point out the + need for aligned storage
    + Douglas Gregor developed 'type_with_alignment', and later Eric Friedman coded + 'aligned_storage', which are the core of the optional class implementation.
    + Andrei Alexandrescu and Brian Parker also worked with aligned storage techniques + and their work influenced the current implementation.
    + Gennadiy Rozental made extensive and important comments which shaped the design.
    + Vesa Karvonen and Douglas Gregor made quite useful comparisons between optional, + variant and any; and made other relevant comments. Douglas Gregor and Peter + Dimov commented on comparisons and evaluation in boolean contexts.
    + Eric Friedman helped understand the issues involved with aligned storage, move/copy + operations and exception safety.
    + Many others have participated with useful comments: Aleksey Gurotov, Kevlin + Henney, David Abrahams, and others I can't recall. +

    +
    +

    Post-formal review:

    +
    +

    William Kempf carrefully considered the originally proposed interface +and suggested the new interface which is currently used. He also started +and fueled the discussion about the analogy optional<>/smart pointer +and about relational operators.
    +Peter Dimov, Joel de Guzman, David Abrahams, Tanton Gibbs and Ian Hanson +focused on the relational semantics of optional (originally undefined); +concluding with the fact that the pointer-like interface doesn't make it +a pointer so it shall have deep relational operators.
    +Augustus Saunders also explored the different relational +semantics between optional<> and a pointer and developed the +OptionalPointee concept as an aid against potential conflicts on generic code.
    +Joel de Guzman noticed that optional<> can be seen as an +API on top of variant<T,nil_t>.
    +Dave Gomboc explained the meaning and usage of the Haskell analog to optional<>: +the Maybe type constructor (analogy originally pointed out by David Sankel).
    +Other comments were posted by Vincent Finn, Anthony Williams, Ed Brey, Rob Stewart, +and others. +

    +
    +
    + +

    Revised January 20, 2003

    +

    © Copyright boost.org 2003. Permission to copy, use, modify, sell and +distribute this document is granted provided this copyright notice appears in +all copies. This document is provided "as is" without express or +implied warranty, and with no claim as to its suitability for any purpose.

    +

    Developed by Fernando Cacciola, +the latest version of this file can be found at www.boost.org, and the boost discussion list at +www.yahoogroups.com/list/boost. +

    + + + diff --git a/include/boost/optional.hpp b/include/boost/optional.hpp new file mode 100644 index 0000000..908f989 --- /dev/null +++ b/include/boost/optional.hpp @@ -0,0 +1,267 @@ +// (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. +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// fernando_cacciola@hotmail.com +// +#ifndef BOOST_OPTIONAL_FLC_19NOV2002_HPP +#define BOOST_OPTIONAL_FLC_19NOV2002_HPP + +#include +#include + +#include "boost/assert.hpp" +#include "boost/type_traits/alignment_of.hpp" +#include "boost/type_traits/type_with_alignment.hpp" + +namespace boost +{ + + namespace optional_detail + { + template + class aligned_storage + { + // Borland ICEs if unnamed unions are used for this! + union dummy_u + { + char data[ sizeof(T) ]; + type_with_alignment< ::boost::alignment_of::value > aligner_; + } dummy_ ; + + public: + + void const* address() const { return &dummy_.data[0]; } + void * address() { return &dummy_.data[0]; } + } ; + } + +template +class optional +{ + typedef optional this_type ; + + typedef optional_detail::aligned_storage storage_type ; + + typedef void (this_type::*unspecified_bool_type)(); + + public : + + typedef T value_type ; + + // Creates an optional uninitialized. + // No-throw + optional () + : + m_initialized(false) {} + + // Creates an optional initialized with 'val'. + // Can throw if T::T(T const&) does + explicit optional ( T const& val ) + : + m_initialized(false) + { + construct(val); + } + + // 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); + } + + // Creates a deep copy of another convertible optional + // Requires a valid conversion from U to T. + // Can throw if T::T(U const&) does + template + explicit optional ( optional const& rhs ) + : + m_initialized(false) + { + if ( rhs ) + construct(*rhs); + } + + // No-throw (assuming T::~T() doesn't) + ~optional() { destroy() ; } + + // Assigns from another optional (deep-copies the rhs value) + // 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); + } + return *this ; + } + + // Assigns from another convertible optional (converts && deep-copies the rhs value) + // Requires a valid conversion from U to T. + // Basic Guarantee: If T::T( U const& ) throws, this is left UNINITIALIZED + template + 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); + } + return *this ; + } + + // Destroys the current value, if any, leaving this UNINITIALIZED + // No-throw (assuming T::~T() doesn't) + 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 ( 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 get() ; } + T* operator->() { BOOST_ASSERT(m_initialized) ; return get() ; } + + // 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 *get() ; } + T& operator *() { BOOST_ASSERT(m_initialized) ; return *get() ; } + + // implicit conversion to "bool" + // No-throw + operator unspecified_bool_type() const { return m_initialized ? &this_type::destroy : 0 ; } + + // 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 ; +} ; + +// Returns a pointer to the value if this is initialized, otherwise, returns NULL. +// No-throw +template +inline +T const* get_pointer ( optional const& opt ) +{ + return opt.get() ; +} + +template +inline +T* get_pointer ( optional& opt ) +{ + return opt.get() ; +} + +// template bool equal_pointees(OP const& x, OP const& y); +// +// Being OP a model of OptionalPointee (either a pointer or an optional): +// +// If both x and y have valid pointees, returns the result of (*x == *y) +// If only one has a valid pointee, returns false. +// If none have valid pointees, returns true. +// No-throw +template +inline +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 +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 !( x == y ) ; } + +// 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 uninitialized, do nothing (no-throw) +template +inline +void swap ( optional& x, optional& y ) +{ + if ( !x && !!y ) + { + x.reset(*y); // Basic guarantee. + y.reset(); + } + else if ( !!x && !y ) + { + y.reset(*x); // Basic guarantee. + x.reset(); + } + else if ( !!x && !!y ) + { + using std::swap ; + swap(*x,*y); + } +} + +} // namespace boost + +#endif + diff --git a/test/optional_test.cpp b/test/optional_test.cpp new file mode 100644 index 0000000..830eae4 --- /dev/null +++ b/test/optional_test.cpp @@ -0,0 +1,898 @@ +// (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 +#include +#include + +#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 +inline void check_uninitialized_const ( optional const& opt ) +{ + BOOST_CHECK( opt == 0 ) ; + BOOST_CHECK( !opt ) ; + BOOST_CHECK( !get_pointer(opt) ) ; + BOOST_CHECK( !opt.get() ) ; +} +template +inline void check_uninitialized ( optional& opt ) +{ + BOOST_CHECK( opt == 0 ) ; + BOOST_CHECK( !opt ) ; + BOOST_CHECK( !get_pointer(opt) ) ; + BOOST_CHECK( !opt.get() ) ; + + check_uninitialized_const(opt); +} + +template +inline void check_initialized_const ( optional const& opt ) +{ + BOOST_CHECK( opt ) ; + BOOST_CHECK( opt != 0 ) ; + BOOST_CHECK ( !!opt ) ; + BOOST_CHECK ( get_pointer(opt) ) ; + BOOST_CHECK ( opt.get() ) ; +} + +template +inline void check_initialized ( optional& 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 +inline void check_value_const ( optional 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 +inline void check_value ( optional& 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 +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 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 oa ( a ) ; + check_is_not_pending_copy( ARG(T) ); + check_initialized(oa); + check_value(oa,a,z); + + + T b(2); + + optional 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 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 oc = d ; +#endif + +} + +// +// Test Direct Value Manipulation +// +template +void test_direct_value_manip( T const* ) +{ + T x(1); + + optional const c_opt0(x) ; + optional 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 +void test_uninitialized_access( T const* ) +{ + optional 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 +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 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 +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 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 +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 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 +void test_throwing_copy_initialization( T const* ) +{ + T z(-1); + T a(1234); + + reset_throw_on_copy( ARG(T) ) ; + + optional 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 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 +void test_throwing_assign_to_uninitialized( T const* ) +{ + T z(-1); + T a(1234); + + reset_throw_on_copy( ARG(T) ) ; + + optional opt0 ; + optional 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 +void test_throwing_assign_to_initialized( T const* ) +{ + T z(-1); + T a(1234); + T b(5678); + + reset_throw_on_copy( ARG(T) ) ; + + optional opt0(a) ; + optional 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 +void test_no_throwing_swap( T const* ) +{ + T z(-1); + T a(1234); + T b(5678); + + reset_throw_on_copy( ARG(T) ) ; + + optional def0 ; + optional def1 ; + optional opt0(a) ; + optional 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 +void test_throwing_swap( T const* ) +{ + T a(1234); + T b(5678); + + reset_throw_on_copy( ARG(T) ) ; + + optional opt0(a) ; + optional 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 +void test_relops( T const* v ) +{ + reset_throw_on_copy( ARG(T) ) ; + + T v0(1); + T v1(2); + T v2(2); + + optional def0 ; + optional def1 ; + optional opt0(v0); + optional opt1(v1); + optional 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 int eat ( T ) { return 0 ; } + +// +// This verifies that operator safe_bool() behaves properly. +// +template +void test_no_implicit_conversions_impl( T const& v ) +{ + optional 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 opt0(c); + + optional opt1(opt0); + BOOST_CHECK(*opt1 == static_cast(c)); + + float f = 1.234 ; + double d = f ; + optional opt2(f) ; + optional opt3 ; + opt3 = opt2 ; + BOOST_CHECK(*opt3 == d); + +#ifdef SHOW_COMPILATION_FAIL_5a + optional opt4(opt0); +#endif + +#ifdef SHOW_COMPILATION_FAIL_5b + optional 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; +} + +