mirror of
https://github.com/boostorg/optional.git
synced 2025-07-29 20:17:21 +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