mirror of
https://github.com/boostorg/optional.git
synced 2025-06-25 03:51:35 +02:00
Initial versions
[SVN r16995]
This commit is contained in:
96
.gitattributes
vendored
Normal file
96
.gitattributes
vendored
Normal file
@ -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
|
982
doc/optional.html
Normal file
982
doc/optional.html
Normal file
@ -0,0 +1,982 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//SoftQuad Software//DTD HoTMetaL PRO 5.0::19981217::extensions to HTML 4.0//EN" "hmpro5.dtd">
|
||||
|
||||
<HTML>
|
||||
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
|
||||
<LINK REL="stylesheet" TYPE="text/css" HREF="../../../../boost.css">
|
||||
<TITLE>Header </TITLE>
|
||||
</HEAD>
|
||||
|
||||
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#800080">
|
||||
<H2><IMG SRC="../../../c++boost.gif" WIDTH="276" HEIGHT="86">Header <<A
|
||||
HREF="../../../boost/optional.hpp">boost/optional.hpp</A>> </H2>
|
||||
|
||||
<H2>Contents</H2>
|
||||
<DL CLASS="page-index">
|
||||
<DT><A HREF="#mot">Motivation</A></DT>
|
||||
<DT><A HREF="#dev">Development</A></DT>
|
||||
<DT><A HREF="#synopsis">Synopsis</A></DT>
|
||||
<DT><A HREF="#semantics">Semantics</A></DT>
|
||||
<DT><A HREF="#examples">Examples</A></DT>
|
||||
<DT><A HREF="#bool">A note about optional<bool></A></DT>
|
||||
<DT><A HREF="#exsafety">Exception Safety Guarantees</A></DT>
|
||||
<DT><A HREF="#requirements">Type requirements</A></DT>
|
||||
<DT><A HREF="#impl">Implementation Notes</A></DT>
|
||||
<DT><A HREF="#porta">Dependencies and Portability</A></DT>
|
||||
<DT><A HREF="#credits">Acknowledgment</A></DT>
|
||||
</DL>
|
||||
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="mot"></A>Motivation</H2>
|
||||
|
||||
<P>Consider these functions which should return a value but which might not have
|
||||
a value to return:</P>
|
||||
<pre>(A) double sqrt(double n );
|
||||
(B) char get_async_input();
|
||||
(C) point polygon::get_any_point_effectively_inside();</pre>
|
||||
<P>There are different approaches to the issue of not having a value to return.</P>
|
||||
<P>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.</P>
|
||||
<P>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.</P>
|
||||
<P>A similar situation occurs with function (C): it is conceptually an error to
|
||||
ask a <i>null-area</i> 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.</P>
|
||||
<P>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...</P>
|
||||
<P>When those values exist, i.e. the return type can hold all meaningful values
|
||||
<i>plus</i> the <i>signal</i> 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>.
|
||||
</P>
|
||||
<P>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:</P>
|
||||
<pre>std::pair<char,bool> get_async_input();
|
||||
std::pair<point,bool> polygon::get_any_point_effectively_inside();</pre>
|
||||
<p>These functions use a consistent interface for dealing with possibly inexistent
|
||||
results:</p>
|
||||
<pre>std::pair<point,bool> p = poly.get_any_point_effectively_inside();
|
||||
if ( p.second )
|
||||
flood_fill(p.first);
|
||||
</pre>
|
||||
|
||||
<P>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.</P>
|
||||
<P>Clearly, we need a better idiom.</P>
|
||||
|
||||
<H2><A NAME="dev"></A>Development</H2>
|
||||
|
||||
<h3>The model</h3>
|
||||
<P>In C++, we can <i>declare</i> an object (a variable) of type T, and we can give this variable
|
||||
an <i>initial value</i> (through an <i>initializer</i>. (c.f. 8.5)).<br>
|
||||
When a declaration includes a non-empty inilializer (an initial value is given), it is said that
|
||||
the object has been <i><b>initialized</b></i>.<br>
|
||||
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
|
||||
<i><b>uninitialized</b></i>. Its actual value exist but has an
|
||||
<i>indeterminate inital value</i> (c.f. 8.5.9).<br>
|
||||
<code>optional<T></code> 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 <i>undefined behvaiour</i>
|
||||
to try to access the value of an uninitialized optional. An uninitialized optional can be <i>assigned</i> 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 <i>uninitialized</i>.</P>
|
||||
<P>In C++ there is no formal notion of uninitialized objects, which
|
||||
means that objects always have an initial value even if indeterminate.<br>
|
||||
As discussed on the previous section, this has a drawback because you need additional
|
||||
information to tell if an object has been effectively initialized.<br>
|
||||
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 <b>discriminated
|
||||
union</b> of T and something else such as a trivial POD or enum.
|
||||
Discriminated unions are often called <i>variants</i>.
|
||||
A variant has a <i>current type</i>, 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: <code>variant<T,nil_t></code>
|
||||
</P>
|
||||
<P>There is precedence for a discriminated union as a model for an optional
|
||||
value: the <a href="http://www.haskell.org/"><u>Haskell</u></a> <b>Maybe</b> builtin type constructor.</p>
|
||||
<p>A discriminated union, which can be seen as a <b>container</b> which has an object of either
|
||||
type T or something else, has <i>exactly</i> the semantics required for a wrapper of optional values:</p>
|
||||
<li><b>deep-copy</b> semantics: copies of the variant implies copies of the contained value.</li>
|
||||
<li><b>deep-relational</b> semantics: comparions between variants matches both current types and values</li>
|
||||
<li>If the variant's current type is T, it is modeling an <i>initialized</i> optional.</li>
|
||||
<li>If the variant's current type is not T, it is modeling an <i>uninitialized</i> optional.</li>
|
||||
<li>Testing if the variant's current type is T models testing if the optional is initialized</li>
|
||||
<li>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</li>
|
||||
<P>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:</P>
|
||||
|
||||
|
||||
<pre>// 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 ) ;
|
||||
</pre>
|
||||
|
||||
<h3>Pointers and optional objects</h3>
|
||||
<P>In C++, unlike many other languages, objects can be referenced <i>indirectly</i>
|
||||
by means of a <b>pointer</b> (or a reference). Pointers have several nice features,
|
||||
two of which are relevant to this development.</p>
|
||||
<p>One is that a pointer has its own <i>pointer value</i>, which in effect
|
||||
references the object being pointed to: the <b>pointee</b>. Consequently,
|
||||
copies of pointers do not involve copies of pointees. This effect results in <i>aliasing</i>:
|
||||
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 <b>shallow-copy</b>, which is opossed to <b>deep-copy</b> were
|
||||
a copy of a wrapper involves a copy of the wrapped object (as with optional<>)<br>
|
||||
Since this is the semantic followed by pointers (and references), shallow-copy
|
||||
(and therefore aliasing) is implied in <b>pointer semantics</b>.</p>
|
||||
<p>The other relevant feature of a pointer is that a pointer can have a <b>null
|
||||
pointer value</b>. This is a <i>special</i> 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.</P>
|
||||
<P>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 <i>refer</i> to optional (that is, possibly inexisting) objects; particularly
|
||||
as optional arguments to a function, but also quite often as optional data members.</P>
|
||||
<P>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: <code>( *p = 2 )</code> and <code>( p->foo())</code>,
|
||||
implicitely convey the notion of optionality, and this information is tied to
|
||||
the <i>syntax</i> 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.<br>
|
||||
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.</P>
|
||||
<p>Such a defacto idiom for referring to optional objects can be formalized in the form of a
|
||||
concept: the <a href="../../utility/OptionalPointee.html">OptionalPointee</a> concept.<br>
|
||||
This concept captures the syntatic usage of operatos *, -> and conversion to bool to convey
|
||||
the notion of optionality.</p>
|
||||
<P>However, pointers are good to <u>refer</u> 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.<br>
|
||||
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. <br>
|
||||
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.
|
||||
</p>
|
||||
<h3>Optional<T> as a model of OptionalPointee</h3>
|
||||
<P>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 <b>deep-copy</b> and
|
||||
<b>deep relational operators</b>, 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 ->().<br>
|
||||
However, it is particularly important that optional<> objects are not mistaken by pointers,
|
||||
they are not. <u><b>optional<> does not model a pointer</b></u>.
|
||||
For instance, optional<> has not shallow-copy so does not alias: two different optionals
|
||||
never refer to the <i>same</i> value (but my have <i>equivalent</i> values).<br>
|
||||
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.<br>
|
||||
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
|
||||
<a href="../../utility/OptionalPointee.html#equal">equal_pointees()</a> instead.
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="synopsis">Synopsis</A></H2>
|
||||
|
||||
<PRE>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 <i>unspecified-bool-type</i>() 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
|
||||
</PRE>
|
||||
|
||||
<HR>
|
||||
|
||||
<h2><A NAME="semantics">Semantics</a></h2>
|
||||
|
||||
<p><i>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.</i></p>
|
||||
<hr>
|
||||
|
||||
<pre>optional<T>::optional();</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Default-Constructs an <b>optional</b>.</p>
|
||||
<p><b>Postconditions:</b> <b>*this</b> is <u>uninitialized</u>.</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<p><b>Notes:</b> T's default constructor <u><i>is not</i></u> called.</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>optional<T> def ;
|
||||
assert ( !def ) ;</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
<pre>explicit optional<T>::optional( T const& v )</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Directly-Constructs an <b>optional</b>.</p>
|
||||
<p><b>Postconditions:</b> <b>*this</b> is <u>initialized</u> and its value is a <i>copy</i> of 'v'.</p>
|
||||
<p><b>Throws:</b> Whatever T::T( T const& ) throws.</p>
|
||||
<p><b>Notes:</b> T::T( T const& ) is called.</p>
|
||||
<p><b>Exception Safety:</b> Exceptions can only be thrown during T::T( T const& );
|
||||
in that case, this constructor has no effect.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
T v;
|
||||
optional<T> opt(v);
|
||||
assert ( *opt == v ) ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
<pre>optional<T>::optional( optional const& rhs );</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Copy-Constructs an <b>optional</b>.</p>
|
||||
<p><b>Postconditions:</b> If <b>rhs</b> is initialized, <b>*this</b> is initialized
|
||||
and its value is a <i>copy</i> of the value of <b>rhs</b>; else <b>*this</b>
|
||||
is uninitialized.</p>
|
||||
<p><b>Throws:</b> Whatever T::T( T const& ) throws.</p>
|
||||
<p><b>Notes:</b> T::T( T const& ) is called if <b>rhs</b> is initialized.</p>
|
||||
<p><b>Exception Safety:</b> Exceptions can only be thrown during T::T( T const& );
|
||||
in that case, this constructor has no effect.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>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 ) ;
|
||||
</pre>
|
||||
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
<pre>explicit template<U> optional<T>::optional( optional<U> const& rhs );</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Copy-Constructs an <b>optional</b>.</p>
|
||||
<p><b>Postconditions:</b> If <b>rhs</b> is initialized, <b>*this</b> is initialized
|
||||
and its value is a <i>copy</i> of the value of <b>rhs</b> <i>converted</i>
|
||||
to type T; else <b>*this</b> is uninitialized.
|
||||
</p>
|
||||
<p><b>Throws:</b> Whatever T::T( U const& ) throws.</p>
|
||||
<p><b>Notes:</b> T::T( U const& ) is called if <b>rhs</b> is initialized, which requires
|
||||
a valid conversion from U to T.
|
||||
</p>
|
||||
<p><b>Exception Safety:</b> Exceptions can only be thrown during T::T( U const& );
|
||||
in that case, this constructor has no effect.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
|
||||
<pre>optional<double> x(123.4);
|
||||
assert ( *x == 123.4 ) ;
|
||||
|
||||
optional<int> y(x) ;
|
||||
assert( *y == 123 ) ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
<pre>optional& optional<T>::operator= ( optional const& rhs ) ;</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Assigns another <b>optional</b> to an <b>optional</b>.</p>
|
||||
<p><b>Postconditions:</b> If <b>rhs</b> is initialized, <b>*this</b> is initialized
|
||||
and its value is a <i>copy</i> of the value of <b>rhs</b>; else <b>*this</b>
|
||||
is uninitialized.
|
||||
</p>
|
||||
<p><b>Throws:</b> Whatever T::T( T const& ) throws.</p>
|
||||
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized
|
||||
using T::~T(), then T::T( T const& ) is called if <b>rhs</b> is initialized.
|
||||
</p>
|
||||
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( T const& );
|
||||
in that case, <b>*this</b> is left <u>uninitialized</u>.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
T v;
|
||||
optional<T> opt(v);
|
||||
optional<T> uninit ;
|
||||
|
||||
opt = uninit ;
|
||||
assert ( !opt ) ;
|
||||
// previous value (copy of 'v') destroyed from within 'opt'.
|
||||
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
<pre>template<U> optional& optional<T>::operator= ( optional<U> const& rhs ) ;</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Assigns another <i>convertible</i> <b>optional</b> to an <b>optional</b>.</p>
|
||||
<p><b>Postconditions:</b> If <b>rhs</b> is initialized, <b>*this</b> is initialized
|
||||
and its value is a <i>copy</i> of the value of <b>rhs</b> <i>converted</i>
|
||||
to type T; else <b>*this</b> is uninitialized.
|
||||
</p>
|
||||
<p><b>Throws:</b> Whatever T::T( U const& ) throws.</p>
|
||||
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized
|
||||
using T::~T(), then T::T( U const& ) is called if <b>rhs</b> is initialized,
|
||||
which requires a valid conversion from U to T.
|
||||
</p>
|
||||
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( U const& );
|
||||
in that case, <b>*this</b> is left <u>uninitialized</u>.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
T v;
|
||||
optional<T> opt0(v);
|
||||
optional<U> opt1;
|
||||
|
||||
opt1 = opt0 ;
|
||||
assert ( *opt1 == static_cast<U>(v) ) ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
<pre>void optional<T>::reset( T const& v ) ;</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Resets the current value.</p>
|
||||
<p><b>Postconditions: </b><b>*this</b> is <u>initialized</u> and its value is
|
||||
a <i>copy</i> of 'v'.
|
||||
</p>
|
||||
<p><b>Throws:</b> Whatever T::T( T const& ) throws.</p>
|
||||
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized
|
||||
using T::~T(), then T::T( T const& ) is called.
|
||||
</p>
|
||||
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( T const& );
|
||||
in that case, <b>*this</b> is left <u>uninitialized</u>.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>optional<T> opt ( some_T ) ;
|
||||
assert( *opt == some_T );
|
||||
opt.reset ( some_other_T ) ;
|
||||
assert( *opt == some_other_T );
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
<pre>void optional<T>::reset() ;</pre>
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> Destroys the current value.</p>
|
||||
<p><b>Postconditions: *this</b> is uninitialized.</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<p><b>Notes:</b> T::~T() is called.</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>optional<T> opt ( some_T ) ;
|
||||
assert( *opt == some_T );
|
||||
opt.reset();
|
||||
assert( !opt );
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
<pre>
|
||||
T const* optional<T>::get() const ;
|
||||
T* optional<T>::get() ;
|
||||
|
||||
inline T const* get_pointer ( optional<T> const& ) ;
|
||||
inline T* get_pointer ( optional<T>&) ;
|
||||
</pre>
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> If <b>*this</b> is initialized, a pointer to the contained
|
||||
value; else 0 (<i>null</i>).
|
||||
</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<p><b>Notes:</b> The contained value is permanently stored within *this, so
|
||||
you should not hold nor delete this pointer
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
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) ) ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
|
||||
<HR>
|
||||
|
||||
|
||||
<pre>
|
||||
T const* optional<T>::operator ->() const ;
|
||||
T* optional<T>::operator ->() ;
|
||||
</pre>
|
||||
<blockquote>
|
||||
<p><b>Requirements: *this</b> is initialized.</p>
|
||||
<p><b>Returns:</b> A pointer to the contained value.</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<p><b>Notes:</b> The requirement is asserted via BOOST_ASSERT().</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
struct X { int mdata ; } ;
|
||||
X x ;
|
||||
optional<X> opt (x);
|
||||
opt->mdata = 2 ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
|
||||
<HR>
|
||||
|
||||
|
||||
<pre>T const& optional<T>::operator*() const ;
|
||||
T& optional<T>::operator*();</pre>
|
||||
<blockquote>
|
||||
<p><b>Requirements: *this</b> is initialized</p>
|
||||
<p><b>Returns:</b> A reference to the contained value</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<p><b>Notes:</b> The requirement is asserted via BOOST_ASSERT().</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>T v ;
|
||||
optional<T> opt ( v );
|
||||
T const& u = *opt;
|
||||
assert ( u == v ) ;
|
||||
T w ;
|
||||
*opt = w ;
|
||||
assert ( *opt == w ) ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
<pre>optional<T>::operator <i>unspecified-bool-type</i>() const ;</pre>
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> An unspecified value which if used on a boolean context is equivalent to (get() != 0)</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<blockquote>
|
||||
<pre>optional<T> def ;
|
||||
assert ( def == 0 );
|
||||
optional<T> opt ( v ) ;
|
||||
assert ( opt );
|
||||
assert ( opt != 0 );
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
|
||||
<pre> bool optional<T>::operator!() ;</pre>
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> If <b>*this</b> is uninitialized, <code>true</code>; else <code>false.</code></p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<p><b>Notes:</b> This operator is provided for those compilers which can't use
|
||||
the <i>unspecified-bool-type</i> operator in certain boolean contexts.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>optional<T> opt ;
|
||||
assert ( !opt );
|
||||
*opt = some_T ;
|
||||
|
||||
// Notice the "double-bang" idiom here.
|
||||
assert ( !!opt ) ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
|
||||
<pre>bool operator == ( optional<T> const& x, optional<T> const& y );</pre>
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> If both <b>x</b> and <b>y</b> are initialied, <code>(*x == *y)</code>.
|
||||
If only x or y is initialized, <code>false</code>. If both are uninitialized, <code>true</code>.
|
||||
</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
<p><b>Notes:</b> Pointers have shallow relational operators while <b>optional</b> 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 <a href="../../utility/OptionalPointee.html#equal">equal_pointees()</a> instead
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
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 ) ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
<pre>bool operator != ( optional<T> const& x, optional<T> const& y );
|
||||
</pre>
|
||||
<blockquote>
|
||||
<p><b>Returns:</b> !( x == y );</p>
|
||||
<p><b>Throws:</b> Nothing.</p>
|
||||
</blockquote>
|
||||
|
||||
<HR>
|
||||
|
||||
<pre>void swap ( optional<T>& x, optional<T>& y );</pre>
|
||||
|
||||
<blockquote>
|
||||
<p><b>Effect:</b> If both <b>x</b> and <b>y</b> are initialized, calls <code>swap(*x,*y)</code>
|
||||
using std::swap.<br>
|
||||
If only one is initialized, say x, calls: <code>y.reset(*x); x.reset();</code><br>
|
||||
If none is initialized, does nothing.
|
||||
</p>
|
||||
<p><b>Postconditions:</b> The states of x and y interchanged.</p>
|
||||
<p><b>Throws:</b> If both are initialized, whatever swap(T&,T&) throws.
|
||||
If only one is initialized, whatever T::T ( T const& ) throws.
|
||||
</p>
|
||||
<p><b>Notes:</b> If both are initialized, swap(T&,T&) is used <i>unqualified</i>
|
||||
but with std::swap introduced in scope.<br>
|
||||
If only one is initialized, T::~T() and T::T( T const& ) is called.
|
||||
</p>
|
||||
<p><b>Exception Safety:</b> If both are initialized, this operation has the exception
|
||||
safety guarantees of swap(T&,T&).<br>
|
||||
If only one is initialized, it has the same <b>basic</b> guarantee as optional<T>::reset( T const& ).
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
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 );
|
||||
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="examples">Examples</A></H2>
|
||||
|
||||
<h3>Optional return values</h3>
|
||||
<PRE>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);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>Optional local variables</h3>
|
||||
<pre>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!");
|
||||
</pre>
|
||||
|
||||
<h3>Optional data members</h3>
|
||||
<pre>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 ;
|
||||
|
||||
};
|
||||
</pre>
|
||||
<h3>Bypassing expensive unnecesary default construction</h3>
|
||||
<pre>class ExpensiveCtor { ... } ;
|
||||
class Fred
|
||||
{
|
||||
Fred() : mLargeVector(10000) {}
|
||||
|
||||
std::vector< optional<ExpensiveCtor> > mLargeVector ;
|
||||
} ;
|
||||
</pre>
|
||||
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="bool">A note about optional<bool></A></H2>
|
||||
<p><code>optional<bool></code> should be used with special caution and consideration.</p>
|
||||
<p>First, it is functionally similar to a tristate boolean (false,maybe,true)
|
||||
-such as <u>boost::tribool</u>-, except that in a tristate boolean, the <i>maybe</i> state
|
||||
<u>represents a valid value</u>, unlike the corresponding state
|
||||
of an uninitialized optional<bool>.<br>
|
||||
It should be carefully considered if an optional bool instead of a tribool is really needed</p>
|
||||
<p>Second, optional<> provides and implicit conversion to bool. This conversion
|
||||
refers to the initialization state and not to the contained value.<br>
|
||||
Using optional<bool> can lead to subtle errors due to the implicit bool conversion:</p>
|
||||
<pre>
|
||||
void foo ( bool v ) ;
|
||||
void bar()
|
||||
{
|
||||
optional<bool> v = try();
|
||||
|
||||
// The following intended to pass the <b>value</b> of 'v' to foo():
|
||||
foo(v);
|
||||
// But instead, the <i>initialization state</i> is passed
|
||||
// due to a typo: it should have been foo(<b>*</b>v).
|
||||
}
|
||||
</pre>
|
||||
<p>The only implicit conversion is to bool, and it is <i>safe</i> in the sense that typical
|
||||
integral promotions don't apply (i.e. if foo() takes an 'int' instead, it won't compile).
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="exsafety">Exception Safety Guarantees</A></H2>
|
||||
<H3><u>Assignment and Reset:</u></H3>
|
||||
<p>Because of the current implementation (see <A HREF="#impl">Implementation Notes</A>),
|
||||
<code> optional<T>::operator=( optional<T> const& )
|
||||
and optional<T>::reset( T const& )</code>
|
||||
can only <i>guarantee</i> the <u>basic exception safety</u>: The lvalue optional is left
|
||||
<u>uninitialized</u> if an exception is thrown (any previous value is <i>first</i>
|
||||
destroyed using T::~T())</p>
|
||||
<p><code>optional<T>::reset()</code> provides the no-throw guarantee (assuming a no-throw T::~T())</p>
|
||||
<p>However, since <code>optional<></code> 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.</p>
|
||||
<pre>//
|
||||
// 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 ) ;
|
||||
}
|
||||
</pre>
|
||||
<H3><u>Swap:</u></H3>
|
||||
<p><code>void swap( optional<T>&, optional<T>& )</code>
|
||||
has the same exception guarantee as <code>swap(T&,T&)</code> when both optionals are initialized.<br>
|
||||
If only one of the optionals is initialized, it gives the same
|
||||
<i>basic</i> exception guarantee as <code>optional<T>::reset( T const& )</code>
|
||||
(since <code>optional<T>::reset()</code> doesn't throw).<br>
|
||||
If none of the optionals is initialized, it has no-throw guarantee since it is a no-op.
|
||||
</p>
|
||||
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="requirements">Type requirements</A></H2>
|
||||
<p>T must be <a href="../../utility/CopyConstructible.html">Copy Constructible</a>
|
||||
and have a no-throw destructor.<br>
|
||||
T <u>is not</u> required to be
|
||||
<a href="http://www.sgi.com/tech/stl/DefaultConstructible.html">Default Constructible</a>
|
||||
</p>
|
||||
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="impl">Implementation Notes</A></H2>
|
||||
<p>optional<T> is currently implemented
|
||||
using a custom aligned storage facility built from <code>alignment_of</code> and
|
||||
<code>type_with_alignment</code> (both from Type Traits).
|
||||
It uses a separate boolean flag to indicate the initialization state.<br>
|
||||
Placement new with T's copy constructor and T's destructor
|
||||
are explicitely used to initialize,copy and destroy optional values.<br>
|
||||
As a result, T's default constructor is effectively by-passed, but the exception
|
||||
guarantess are basic.<br>
|
||||
It is planned to replace the current implementation with another with
|
||||
stronger exception safety, such as a future boost::variant<T,nil_t>.
|
||||
</p>
|
||||
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="porta">Dependencies and Portability</A></H2>
|
||||
|
||||
<p>The implementation uses <code>type_traits/alignement_of.hpp</code>
|
||||
and <code>type_traits/type_with_alignement.hpp</code></p>
|
||||
<p>It has been tested on bcc5.5.1, vc6.0 and gcc2.95.2</p>
|
||||
|
||||
<HR>
|
||||
|
||||
<H2><A NAME="credits">Acknowledgments</A></H2>
|
||||
<p>Pre-formal review:</p>
|
||||
<blockquote>
|
||||
<p>Peter Dimov suggested the name 'optional', and was the first to point out the
|
||||
need for aligned storage<br>
|
||||
Douglas Gregor developed 'type_with_alignment', and later Eric Friedman coded
|
||||
'aligned_storage', which are the core of the optional class implementation.<br>
|
||||
Andrei Alexandrescu and Brian Parker also worked with aligned storage techniques
|
||||
and their work influenced the current implementation.<br>
|
||||
Gennadiy Rozental made extensive and important comments which shaped the design.<br>
|
||||
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.<br>
|
||||
Eric Friedman helped understand the issues involved with aligned storage, move/copy
|
||||
operations and exception safety.<br>
|
||||
Many others have participated with useful comments: Aleksey Gurotov, Kevlin
|
||||
Henney, David Abrahams, and others I can't recall.
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>Post-formal review:</p>
|
||||
<blockquote>
|
||||
<p>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.<br>
|
||||
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.<br>
|
||||
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.<br>
|
||||
Joel de Guzman noticed that optional<> can be seen as an
|
||||
API on top of variant<T,nil_t>.<br>
|
||||
Dave Gomboc explained the meaning and usage of the Haskell analog to optional<>:
|
||||
the Maybe type constructor (analogy originally pointed out by David Sankel).<br>
|
||||
Other comments were posted by Vincent Finn, Anthony Williams, Ed Brey, Rob Stewart,
|
||||
and others.
|
||||
</p>
|
||||
</blockquote>
|
||||
<HR>
|
||||
|
||||
<P>Revised January 20, 2003</P>
|
||||
<P>© 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.</P>
|
||||
<P>Developed by <A HREF="mailto:fernando_cacciola@hotmail.com">Fernando Cacciola</A>,
|
||||
the latest version of this file can be found at <A
|
||||
HREF="http://www.boost.org">www.boost.org</A>, and the boost discussion list at
|
||||
<A
|
||||
HREF="http://www.yahoogroups.com/list/boost">www.yahoogroups.com/list/boost</A>.
|
||||
</P>
|
||||
</u></BODY>
|
||||
</HTML>
|
||||
|
267
include/boost/optional.hpp
Normal file
267
include/boost/optional.hpp
Normal file
@ -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<new>
|
||||
#include<algorithm>
|
||||
|
||||
#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 T>
|
||||
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<T>::value > aligner_;
|
||||
} dummy_ ;
|
||||
|
||||
public:
|
||||
|
||||
void const* address() const { return &dummy_.data[0]; }
|
||||
void * address() { return &dummy_.data[0]; }
|
||||
} ;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
class optional
|
||||
{
|
||||
typedef optional<T> this_type ;
|
||||
|
||||
typedef optional_detail::aligned_storage<T> storage_type ;
|
||||
|
||||
typedef void (this_type::*unspecified_bool_type)();
|
||||
|
||||
public :
|
||||
|
||||
typedef T value_type ;
|
||||
|
||||
// Creates an optional<T> uninitialized.
|
||||
// No-throw
|
||||
optional ()
|
||||
:
|
||||
m_initialized(false) {}
|
||||
|
||||
// Creates an optional<T> 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<T>
|
||||
// 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<U>
|
||||
// Requires a valid conversion from U to T.
|
||||
// Can throw if T::T(U const&) does
|
||||
template<class U>
|
||||
explicit optional ( optional<U> const& rhs )
|
||||
:
|
||||
m_initialized(false)
|
||||
{
|
||||
if ( rhs )
|
||||
construct(*rhs);
|
||||
}
|
||||
|
||||
// No-throw (assuming T::~T() doesn't)
|
||||
~optional() { destroy() ; }
|
||||
|
||||
// Assigns from another optional<T> (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<U> (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<class U>
|
||||
optional& operator= ( optional<U> 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<T const*>(m_storage.address()) : 0 ; }
|
||||
T* get() { return m_initialized ? static_cast<T*> (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<class T>
|
||||
inline
|
||||
T const* get_pointer ( optional<T> const& opt )
|
||||
{
|
||||
return opt.get() ;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
T* get_pointer ( optional<T>& opt )
|
||||
{
|
||||
return opt.get() ;
|
||||
}
|
||||
|
||||
// template<class OP> 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<class OptionalPointee>
|
||||
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<class T>
|
||||
inline
|
||||
bool operator == ( optional<T> const& x, optional<T> const& y )
|
||||
{ return equal_pointees(x,y); }
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
bool operator != ( optional<T> const& x, optional<T> 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<class T>
|
||||
inline
|
||||
void swap ( optional<T>& x, optional<T>& 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
|
||||
|
898
test/optional_test.cpp
Normal file
898
test/optional_test.cpp
Normal file
@ -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<iostream>
|
||||
#include<stdexcept>
|
||||
#include<string>
|
||||
|
||||
#define BOOST_ENABLE_ASSERT_HANDLER
|
||||
#include "boost/optional.hpp"
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
#include "boost/test/minimal.hpp"
|
||||
|
||||
namespace boost {
|
||||
|
||||
bool assertion_failed (char const * expr, char const * func, char const * file, long line)
|
||||
{
|
||||
throw std::logic_error( std::string("Boost Error: assertion failed at\nfile: ")
|
||||
+ std::string(file)
|
||||
+ std::string("\nfunction: ")
|
||||
+ std::string(func)
|
||||
) ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using boost::optional ;
|
||||
|
||||
#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
|
||||
using boost::swap ;
|
||||
using boost::get_pointer ;
|
||||
#endif
|
||||
|
||||
#define TRACE_LIFETIME(msg) if ( trace_lifetime ) { std::cout << msg << std::endl ; }
|
||||
|
||||
#define ARG(T) (static_cast< T const* >(0))
|
||||
|
||||
//#define SHOW_COMPILATION_FAIL_1
|
||||
//#define SHOW_COMPILATION_FAIL_2
|
||||
//#define SHOW_COMPILATION_FAIL_3
|
||||
//#define SHOW_COMPILATION_FAIL_4a
|
||||
//#define SHOW_COMPILATION_FAIL_4b
|
||||
//#define SHOW_COMPILATION_FAIL_5a
|
||||
//#define SHOW_COMPILATION_FAIL_5b
|
||||
|
||||
//
|
||||
// Helper class used to verify the lifetime managment of the values held by optional
|
||||
//
|
||||
class X
|
||||
{
|
||||
public :
|
||||
|
||||
X ( int av ) : v(av)
|
||||
{
|
||||
++ count ;
|
||||
|
||||
TRACE_LIFETIME ( "X::X(" << av << "). this=" << this ) ;
|
||||
}
|
||||
|
||||
X ( X const& rhs ) : v(rhs.v)
|
||||
{
|
||||
pending_copy = false ;
|
||||
|
||||
TRACE_LIFETIME ( "X::X( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
|
||||
|
||||
if ( throw_on_copy )
|
||||
{
|
||||
TRACE_LIFETIME ( "throwing exception in X's copy ctor" ) ;
|
||||
throw 0 ;
|
||||
}
|
||||
|
||||
++ count ;
|
||||
}
|
||||
|
||||
~X()
|
||||
{
|
||||
pending_dtor = false ;
|
||||
|
||||
-- count ;
|
||||
|
||||
TRACE_LIFETIME ( "X::~X(). v=" << v << " this=" << this );
|
||||
|
||||
}
|
||||
|
||||
X& operator= ( X const& rhs )
|
||||
{
|
||||
v = rhs.v ;
|
||||
|
||||
TRACE_LIFETIME ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
|
||||
|
||||
return *this ;
|
||||
}
|
||||
|
||||
friend bool operator == ( X const& a, X const& b )
|
||||
{ return a.v == b.v ; }
|
||||
|
||||
friend bool operator != ( X const& a, X const& b )
|
||||
{ return a.v != b.v ; }
|
||||
|
||||
friend bool operator < ( X const& a, X const& b )
|
||||
{ return a.v < b.v ; }
|
||||
|
||||
int V() const { return v ; }
|
||||
int& V() { return v ; }
|
||||
|
||||
static int count ;
|
||||
static bool pending_copy ;
|
||||
static bool pending_dtor ;
|
||||
static bool trace_lifetime ;
|
||||
static bool throw_on_copy ;
|
||||
|
||||
private :
|
||||
|
||||
int v ;
|
||||
|
||||
private :
|
||||
|
||||
X() ;
|
||||
} ;
|
||||
int X::count = 0 ;
|
||||
bool X::trace_lifetime = false ;
|
||||
bool X::pending_copy = false ;
|
||||
bool X::pending_dtor = false ;
|
||||
bool X::throw_on_copy = false ;
|
||||
|
||||
inline void set_pending_copy ( X const* x ) { X::pending_copy = true ; }
|
||||
inline void set_pending_dtor ( X const* x ) { X::pending_dtor = true ; }
|
||||
inline void set_throw_on_copy ( X const* x ) { X::throw_on_copy = true ; }
|
||||
inline void reset_throw_on_copy ( X const* x ) { X::throw_on_copy = false ; }
|
||||
inline void check_is_pending_copy ( X const* x ) { BOOST_CHECK( X::pending_copy ) ; }
|
||||
inline void check_is_pending_dtor ( X const* x ) { BOOST_CHECK( X::pending_dtor ) ; }
|
||||
inline void check_is_not_pending_copy( X const* x ) { BOOST_CHECK( !X::pending_copy ) ; }
|
||||
inline void check_is_not_pending_dtor( X const* x ) { BOOST_CHECK( !X::pending_dtor ) ; }
|
||||
inline void check_instance_count ( int c, X const* x ) { BOOST_CHECK( X::count == c ) ; }
|
||||
inline int get_instance_count ( X const* x ) { return X::count ; }
|
||||
|
||||
inline void set_pending_copy (...) {}
|
||||
inline void set_pending_dtor (...) {}
|
||||
inline void set_throw_on_copy (...) {}
|
||||
inline void reset_throw_on_copy (...) {}
|
||||
inline void check_is_pending_copy (...) {}
|
||||
inline void check_is_pending_dtor (...) {}
|
||||
inline void check_is_not_pending_copy(...) {}
|
||||
inline void check_is_not_pending_dtor(...) {}
|
||||
inline void check_instance_count (...) {}
|
||||
inline int get_instance_count (...) { return 0 ; }
|
||||
|
||||
|
||||
template<class T>
|
||||
inline void check_uninitialized_const ( optional<T> const& opt )
|
||||
{
|
||||
BOOST_CHECK( opt == 0 ) ;
|
||||
BOOST_CHECK( !opt ) ;
|
||||
BOOST_CHECK( !get_pointer(opt) ) ;
|
||||
BOOST_CHECK( !opt.get() ) ;
|
||||
}
|
||||
template<class T>
|
||||
inline void check_uninitialized ( optional<T>& opt )
|
||||
{
|
||||
BOOST_CHECK( opt == 0 ) ;
|
||||
BOOST_CHECK( !opt ) ;
|
||||
BOOST_CHECK( !get_pointer(opt) ) ;
|
||||
BOOST_CHECK( !opt.get() ) ;
|
||||
|
||||
check_uninitialized_const(opt);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void check_initialized_const ( optional<T> const& opt )
|
||||
{
|
||||
BOOST_CHECK( opt ) ;
|
||||
BOOST_CHECK( opt != 0 ) ;
|
||||
BOOST_CHECK ( !!opt ) ;
|
||||
BOOST_CHECK ( get_pointer(opt) ) ;
|
||||
BOOST_CHECK ( opt.get() ) ;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void check_initialized ( optional<T>& opt )
|
||||
{
|
||||
BOOST_CHECK( opt ) ;
|
||||
BOOST_CHECK( opt != 0 ) ;
|
||||
BOOST_CHECK ( !!opt ) ;
|
||||
BOOST_CHECK ( get_pointer(opt) ) ;
|
||||
BOOST_CHECK ( opt.get() ) ;
|
||||
|
||||
check_initialized_const(opt);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void check_value_const ( optional<T> const& opt, T const& v, T const& z )
|
||||
{
|
||||
BOOST_CHECK( *opt == v ) ;
|
||||
BOOST_CHECK( *opt != z ) ;
|
||||
BOOST_CHECK( (*(opt.operator->()) == v) ) ;
|
||||
BOOST_CHECK( *get_pointer(opt) == v ) ;
|
||||
BOOST_CHECK( *opt.get() == v ) ;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void check_value ( optional<T>& opt, T const& v, T const& z )
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
// For some reason, VC6.0 is creating a temporary while evaluating (*opt == v),
|
||||
// so we need to turn throw on copy off first.
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
#endif
|
||||
|
||||
BOOST_CHECK( *opt == v ) ;
|
||||
BOOST_CHECK( *opt != z ) ;
|
||||
BOOST_CHECK( (*(opt.operator->()) == v) ) ;
|
||||
BOOST_CHECK( *get_pointer(opt) == v ) ;
|
||||
BOOST_CHECK( *opt.get() == v ) ;
|
||||
|
||||
check_value_const(opt,v,z);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Basic test.
|
||||
// Check ordinary functionality:
|
||||
// Initialization, assignment, comparison and value-accessing.
|
||||
//
|
||||
template<class T>
|
||||
void test_basics( T const* )
|
||||
{
|
||||
std::cout << std::endl ;
|
||||
|
||||
T z(-1);
|
||||
|
||||
T a(1);
|
||||
|
||||
// Default construction.
|
||||
// 'def' state is Uninitialized.
|
||||
// T::T() is not called (and it is not even defined)
|
||||
optional<T> def ;
|
||||
check_uninitialized(def);
|
||||
|
||||
// Direct initialization.
|
||||
// 'oa' state is Initialized with 'a'
|
||||
// T::T( T const& x ) is used.
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
optional<T> oa ( a ) ;
|
||||
check_is_not_pending_copy( ARG(T) );
|
||||
check_initialized(oa);
|
||||
check_value(oa,a,z);
|
||||
|
||||
|
||||
T b(2);
|
||||
|
||||
optional<T> ob ;
|
||||
|
||||
// Value-Assignment upon Uninitialized optional.
|
||||
// T::T ( T const& x ) is used.
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
ob.reset(a) ;
|
||||
check_is_not_pending_copy( ARG(T) ) ;
|
||||
check_initialized(ob);
|
||||
check_value(ob,a,z);
|
||||
|
||||
// Value-Assignment upon Initialized optional.
|
||||
// This uses T::operator= ( T const& x ) directly
|
||||
// on the reference returned by operator*()
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
*ob = b ;
|
||||
check_is_pending_dtor( ARG(T) ) ;
|
||||
check_is_pending_copy( ARG(T) ) ;
|
||||
check_initialized(ob);
|
||||
check_value(ob,b,z);
|
||||
|
||||
// Assignment initialization.
|
||||
// T::T ( T const& x ) is used to copy new value.
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
optional<T> const oa2 = oa ;
|
||||
check_is_not_pending_copy( ARG(T) ) ;
|
||||
check_initialized_const(oa2);
|
||||
check_value_const(oa2,a,z);
|
||||
|
||||
// Assignment
|
||||
// T::~T() is used to destroy previous value in ob.
|
||||
// T::T ( T const& x ) is used to copy new value.
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
oa = ob ;
|
||||
check_is_not_pending_dtor( ARG(T) ) ;
|
||||
check_is_not_pending_copy( ARG(T) ) ;
|
||||
check_initialized(oa);
|
||||
check_value(oa,b,z);
|
||||
|
||||
// Uninitializing Assignment upon Initialized Optional
|
||||
// T::~T() is used to destroy previous value in oa.
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
oa = def ;
|
||||
check_is_not_pending_dtor( ARG(T) ) ;
|
||||
check_is_pending_copy ( ARG(T) ) ;
|
||||
check_uninitialized(oa);
|
||||
|
||||
// Uninitializing Assignment upon Uninitialized Optional
|
||||
// (Dtor is not called this time)
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
oa = def ;
|
||||
check_is_pending_dtor( ARG(T) ) ;
|
||||
check_is_pending_copy( ARG(T) ) ;
|
||||
check_uninitialized(oa);
|
||||
|
||||
// Deinitialization of Initialized Optional
|
||||
// T::~T() is used to destroy previous value in ob.
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
ob.reset();
|
||||
check_is_not_pending_dtor( ARG(T) ) ;
|
||||
check_uninitialized(ob);
|
||||
|
||||
// Deinitialization of Uninitialized Optional
|
||||
// (Dtor is not called this time)
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
ob.reset();
|
||||
check_is_pending_dtor( ARG(T) ) ;
|
||||
check_uninitialized(ob);
|
||||
|
||||
#ifdef SHOW_COMPILATION_FAIL_1
|
||||
// This is illegal since 'oa2' is const.
|
||||
*oa2 = oa ;
|
||||
#endif
|
||||
|
||||
#ifdef SHOW_COMPILATION_FAIL_2
|
||||
T c(3);
|
||||
// Direct Value Assignment is not allowed.
|
||||
// Use operator*() instead.
|
||||
oa = c ;
|
||||
#endif
|
||||
|
||||
#ifdef SHOW_COMPILATION_FAIL_3
|
||||
T d(4);
|
||||
// Direct Value Construction is explicit.
|
||||
optional<T> oc = d ;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Test Direct Value Manipulation
|
||||
//
|
||||
template<class T>
|
||||
void test_direct_value_manip( T const* )
|
||||
{
|
||||
T x(1);
|
||||
|
||||
optional<T> const c_opt0(x) ;
|
||||
optional<T> opt0(x);
|
||||
|
||||
BOOST_CHECK( c_opt0->V() == x.V() ) ;
|
||||
BOOST_CHECK( opt0->V() == x.V() ) ;
|
||||
|
||||
BOOST_CHECK( (*c_opt0).V() == x.V() ) ;
|
||||
BOOST_CHECK( (* opt0).V() == x.V() ) ;
|
||||
|
||||
T y(2);
|
||||
*opt0 = y ;
|
||||
BOOST_CHECK( (*opt0).V() == y.V() ) ;
|
||||
|
||||
BOOST_CHECK( x < (*opt0) ) ;
|
||||
}
|
||||
|
||||
//
|
||||
// Test Uninitialized access assert
|
||||
//
|
||||
template<class T>
|
||||
void test_uninitialized_access( T const* )
|
||||
{
|
||||
optional<T> def ;
|
||||
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should throw becasue 'def' is uninitialized
|
||||
T const& n = *def ;
|
||||
(n);
|
||||
passed = true ;
|
||||
}
|
||||
catch (...) {}
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
passed = false ;
|
||||
try
|
||||
{
|
||||
T v(1) ; (v);
|
||||
// This should throw becasue 'def' is uninitialized
|
||||
*def = v ;
|
||||
passed = true ;
|
||||
}
|
||||
catch (...) {}
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
passed = false ;
|
||||
try
|
||||
{
|
||||
// This should throw becasue 'def' is uninitialized
|
||||
T v = *(def.operator->()) ;
|
||||
(v);
|
||||
passed = true ;
|
||||
}
|
||||
catch (...) {}
|
||||
BOOST_CHECK(!passed);
|
||||
}
|
||||
|
||||
//
|
||||
// Test Direct Initialization of optional for a T with throwing copy-ctor.
|
||||
//
|
||||
template<class T>
|
||||
void test_throwing_direct_init( T const* )
|
||||
{
|
||||
T a(1234);
|
||||
|
||||
int count = get_instance_count( ARG(T) ) ;
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should:
|
||||
// Attempt to copy construct 'a' and throw.
|
||||
// 'opt' won't be constructed.
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
optional<T> opt(a) ;
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ){}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
check_is_not_pending_copy( ARG(T) );
|
||||
check_instance_count(count, ARG(T) );
|
||||
}
|
||||
|
||||
//
|
||||
// Test Value Assignment to an Uninitialized optional for a T with a throwing copy-ctor
|
||||
//
|
||||
template<class T>
|
||||
void test_throwing_val_assign_on_uninitialized( T const* )
|
||||
{
|
||||
T a(1234);
|
||||
|
||||
int count = get_instance_count( ARG(T) ) ;
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
optional<T> opt ;
|
||||
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should:
|
||||
// Attempt to copy construct 'a' and throw.
|
||||
// opt should be left uninitialized.
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
opt.reset( a );
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
check_is_not_pending_copy( ARG(T) );
|
||||
check_instance_count(count, ARG(T) );
|
||||
check_uninitialized(opt);
|
||||
}
|
||||
|
||||
//
|
||||
// Test Value Reset on an Initialized optional for a T with a throwing copy-ctor
|
||||
//
|
||||
template<class T>
|
||||
void test_throwing_val_assign_on_initialized( T const* )
|
||||
{
|
||||
T z(-1);
|
||||
T a(1234);
|
||||
T b(5678);
|
||||
|
||||
int count = get_instance_count( ARG(T) ) ;
|
||||
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
optional<T> opt ( b ) ;
|
||||
++ count ;
|
||||
|
||||
check_instance_count(count, ARG(T) );
|
||||
|
||||
check_value(opt,b,z);
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should:
|
||||
// Attempt to copy construct 'a' and throw.
|
||||
// opt should be left uninitialized (even though it was initialized)
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
opt.reset ( a ) ;
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
-- count ;
|
||||
|
||||
check_is_not_pending_dtor( ARG(T) );
|
||||
check_is_not_pending_copy( ARG(T) );
|
||||
check_instance_count(count, ARG(T) );
|
||||
check_uninitialized(opt);
|
||||
}
|
||||
|
||||
//
|
||||
// Test Copy Initialization from an Initialized optional for a T with a throwing copy-ctor
|
||||
//
|
||||
template<class T>
|
||||
void test_throwing_copy_initialization( T const* )
|
||||
{
|
||||
T z(-1);
|
||||
T a(1234);
|
||||
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
optional<T> opt (a);
|
||||
|
||||
int count = get_instance_count( ARG(T) ) ;
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should:
|
||||
// Attempt to copy construct 'opt' and throw.
|
||||
// opt1 won't be constructed.
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
optional<T> opt1 = opt ;
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
check_is_not_pending_copy( ARG(T) );
|
||||
check_instance_count(count, ARG(T) );
|
||||
|
||||
// Nothing should have happened to the source optional.
|
||||
check_initialized(opt);
|
||||
check_value(opt,a,z);
|
||||
}
|
||||
|
||||
//
|
||||
// Test Assignment to an Uninitialized optional from an Initialized optional
|
||||
// for a T with a throwing copy-ctor
|
||||
//
|
||||
template<class T>
|
||||
void test_throwing_assign_to_uninitialized( T const* )
|
||||
{
|
||||
T z(-1);
|
||||
T a(1234);
|
||||
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
optional<T> opt0 ;
|
||||
optional<T> opt1(a) ;
|
||||
|
||||
int count = get_instance_count( ARG(T) ) ;
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should:
|
||||
// Attempt to copy construct 'opt1.value()' into opt0 and throw.
|
||||
// opt0 should be left uninitialized.
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
opt0 = opt1 ;
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
check_is_not_pending_copy( ARG(T) );
|
||||
check_instance_count(count, ARG(T) );
|
||||
check_uninitialized(opt0);
|
||||
}
|
||||
|
||||
//
|
||||
// Test Assignment to an Initialized optional from an Initialized optional
|
||||
// for a T with a throwing copy-ctor
|
||||
//
|
||||
template<class T>
|
||||
void test_throwing_assign_to_initialized( T const* )
|
||||
{
|
||||
T z(-1);
|
||||
T a(1234);
|
||||
T b(5678);
|
||||
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
optional<T> opt0(a) ;
|
||||
optional<T> opt1(b) ;
|
||||
|
||||
int count = get_instance_count( ARG(T) ) ;
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should:
|
||||
// Attempt to copy construct 'opt1.value()' into opt0 and throw.
|
||||
// opt0 should be left uninitialized (even though it was initialized)
|
||||
set_pending_dtor( ARG(T) ) ;
|
||||
set_pending_copy( ARG(T) ) ;
|
||||
opt0 = opt1 ;
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
-- count ;
|
||||
|
||||
check_is_not_pending_dtor( ARG(T) );
|
||||
check_is_not_pending_copy( ARG(T) );
|
||||
check_instance_count(count, ARG(T) );
|
||||
check_uninitialized(opt0);
|
||||
}
|
||||
|
||||
//
|
||||
// Test swap in a no-throwing case
|
||||
//
|
||||
template<class T>
|
||||
void test_no_throwing_swap( T const* )
|
||||
{
|
||||
T z(-1);
|
||||
T a(1234);
|
||||
T b(5678);
|
||||
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
optional<T> def0 ;
|
||||
optional<T> def1 ;
|
||||
optional<T> opt0(a) ;
|
||||
optional<T> opt1(b) ;
|
||||
|
||||
int count = get_instance_count( ARG(T) ) ;
|
||||
|
||||
using boost::swap ;
|
||||
|
||||
swap(def0,def1);
|
||||
check_uninitialized(def0);
|
||||
check_uninitialized(def1);
|
||||
|
||||
swap(def0,opt0);
|
||||
check_uninitialized(opt0);
|
||||
check_initialized(def0);
|
||||
check_value(def0,a,z);
|
||||
|
||||
// restore def0 and opt0
|
||||
swap(def0,opt0);
|
||||
|
||||
swap(opt0,opt1);
|
||||
check_instance_count(count, ARG(T) );
|
||||
check_initialized(opt0);
|
||||
check_initialized(opt1);
|
||||
check_value(opt0,b,z);
|
||||
check_value(opt1,a,z);
|
||||
}
|
||||
|
||||
//
|
||||
// Test swap in a throwing case
|
||||
//
|
||||
template<class T>
|
||||
void test_throwing_swap( T const* )
|
||||
{
|
||||
T a(1234);
|
||||
T b(5678);
|
||||
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
optional<T> opt0(a) ;
|
||||
optional<T> opt1(b) ;
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
//
|
||||
// Case 1: Both Initialized.
|
||||
//
|
||||
bool passed = false ;
|
||||
try
|
||||
{
|
||||
// This should attempt to swap optionals and fail at swap(X&,X&).
|
||||
swap(opt0,opt1);
|
||||
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
// Assuming swap(T&,T&) has at least the basic guarantee, these should hold.
|
||||
BOOST_CHECK( ( !opt0 || ( !!opt0 && ( ( *opt0 == a ) || ( *opt0 == b ) ) ) ) ) ;
|
||||
BOOST_CHECK( ( !opt1 || ( !!opt1 && ( ( *opt1 == a ) || ( *opt1 == b ) ) ) ) ) ;
|
||||
|
||||
//
|
||||
// Case 2: Only one Initialized.
|
||||
//
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
opt0.reset();
|
||||
opt1.reset(a);
|
||||
|
||||
set_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
passed = false ;
|
||||
try
|
||||
{
|
||||
// This should attempt to swap optionals and fail at opt0.reset(*opt1)
|
||||
// opt0 should be left uninitialized and opt1 unchanged.
|
||||
swap(opt0,opt1);
|
||||
|
||||
passed = true ;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
BOOST_CHECK(!passed);
|
||||
|
||||
check_uninitialized(opt0);
|
||||
check_initialized(opt1);
|
||||
check_value(opt1,a,b);
|
||||
}
|
||||
|
||||
//
|
||||
// This verifies relational operators.
|
||||
//
|
||||
template<class T>
|
||||
void test_relops( T const* v )
|
||||
{
|
||||
reset_throw_on_copy( ARG(T) ) ;
|
||||
|
||||
T v0(1);
|
||||
T v1(2);
|
||||
T v2(2);
|
||||
|
||||
optional<T> def0 ;
|
||||
optional<T> def1 ;
|
||||
optional<T> opt0(v0);
|
||||
optional<T> opt1(v1);
|
||||
optional<T> opt2(v2);
|
||||
|
||||
#ifdef SHOW_COMPILATION_FAIL_4a
|
||||
// You can compare against 0 or against another optional<>,
|
||||
// but not against another value
|
||||
if ( def0 == 1 ) ;
|
||||
#endif
|
||||
|
||||
#ifdef SHOW_COMPILATION_FAIL_4b
|
||||
if ( def0 != 1 ) ;
|
||||
#endif
|
||||
|
||||
// Check identity
|
||||
BOOST_CHECK ( def0 == def0 ) ;
|
||||
BOOST_CHECK ( opt0 == opt0 ) ;
|
||||
BOOST_CHECK ( !(def0 != def0) ) ;
|
||||
BOOST_CHECK ( !(opt0 != opt0) ) ;
|
||||
|
||||
// If both are uininitalized they compare equal
|
||||
BOOST_CHECK ( def0 == def1 ) ;
|
||||
BOOST_CHECK ( !(def0 != def1) ) ;
|
||||
|
||||
// If only one is initialized they compare unequal
|
||||
BOOST_CHECK ( def0 != opt0 ) ;
|
||||
BOOST_CHECK ( !(def1 == opt1) ) ;
|
||||
|
||||
// If both are initialized, values are compared
|
||||
BOOST_CHECK ( opt0 != opt1 ) ;
|
||||
BOOST_CHECK ( opt1 == opt2 ) ;
|
||||
}
|
||||
|
||||
void test_with_builtin_types()
|
||||
{
|
||||
test_basics( ARG(double) );
|
||||
test_uninitialized_access( ARG(double) );
|
||||
test_no_throwing_swap( ARG(double) );
|
||||
test_relops( ARG(double) ) ;
|
||||
}
|
||||
|
||||
void test_with_class_type()
|
||||
{
|
||||
test_basics( ARG(X) );
|
||||
test_direct_value_manip( ARG(X) );
|
||||
test_uninitialized_access( ARG(X) );
|
||||
test_throwing_direct_init( ARG(X) );
|
||||
test_throwing_val_assign_on_uninitialized( ARG(X) );
|
||||
test_throwing_val_assign_on_initialized( ARG(X) );
|
||||
test_throwing_copy_initialization( ARG(X) );
|
||||
test_throwing_assign_to_uninitialized( ARG(X) );
|
||||
test_throwing_assign_to_initialized( ARG(X) );
|
||||
test_no_throwing_swap( ARG(X) );
|
||||
test_throwing_swap( ARG(X) );
|
||||
test_relops( ARG(X) ) ;
|
||||
|
||||
BOOST_CHECK ( X::count == 0 ) ;
|
||||
}
|
||||
|
||||
int eat ( char ) { return 1 ; }
|
||||
int eat ( int ) { return 1 ; }
|
||||
int eat ( void const* ) { return 1 ; }
|
||||
|
||||
template<class T> int eat ( T ) { return 0 ; }
|
||||
|
||||
//
|
||||
// This verifies that operator safe_bool() behaves properly.
|
||||
//
|
||||
template<class T>
|
||||
void test_no_implicit_conversions_impl( T const& v )
|
||||
{
|
||||
optional<T> def ;
|
||||
BOOST_CHECK ( eat(def) == 0 ) ;
|
||||
}
|
||||
|
||||
void test_no_implicit_conversions()
|
||||
{
|
||||
char c = 0 ;
|
||||
int i = 0 ;
|
||||
void const* p = 0 ;
|
||||
|
||||
test_no_implicit_conversions_impl(c);
|
||||
test_no_implicit_conversions_impl(i);
|
||||
test_no_implicit_conversions_impl(p);
|
||||
}
|
||||
|
||||
struct A {} ;
|
||||
void test_conversions()
|
||||
{
|
||||
char c = 123 ;
|
||||
optional<char> opt0(c);
|
||||
|
||||
optional<int> opt1(opt0);
|
||||
BOOST_CHECK(*opt1 == static_cast<int>(c));
|
||||
|
||||
float f = 1.234 ;
|
||||
double d = f ;
|
||||
optional<float> opt2(f) ;
|
||||
optional<double> opt3 ;
|
||||
opt3 = opt2 ;
|
||||
BOOST_CHECK(*opt3 == d);
|
||||
|
||||
#ifdef SHOW_COMPILATION_FAIL_5a
|
||||
optional<A> opt4(opt0);
|
||||
#endif
|
||||
|
||||
#ifdef SHOW_COMPILATION_FAIL_5b
|
||||
optional<A> opt5 ;
|
||||
opt5 = opt0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int test_main( int, char* [] )
|
||||
{
|
||||
try
|
||||
{
|
||||
test_with_class_type();
|
||||
test_with_builtin_types();
|
||||
test_no_implicit_conversions();
|
||||
test_conversions();
|
||||
}
|
||||
catch (... )
|
||||
{
|
||||
BOOST_ERROR("Unexpected Exception caught!");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user