diff --git a/doc/optional.qbk b/doc/00_optional.qbk similarity index 53% rename from doc/optional.qbk rename to doc/00_optional.qbk index 3a74566..abf5fd7 100644 --- a/doc/optional.qbk +++ b/doc/00_optional.qbk @@ -46,23 +46,39 @@ Distributed under the Boost Software License, Version 1.0. [section Introduction] -This library can be used to represent 'optional' (or 'nullable') objects that can be safely passed by value: +Class template `optional` is a wrapper for representing 'optional' (or 'nullable') objects who may not (yet) contain a valid value. Optional objects offer full value semantics; they are good for passing by value and usage inside STL containers. This is a header-only library. - optional readInt(); // this function may return either an int or a not-an-int - - if (optional oi = readInt()) // did I get a real int - cout << "my int is: " << *oi; // use my int - else - cout << "I have no int"; - +[section Problem] +Suppose we want to read a parameter form a config file which represents some integral value, let's call it `"MaxValue"`. It is possible that this parameter is not specified; such situation is no error. It is valid to not specify the parameter and in that case the program is supposed to behave slightly different. Also suppose that any possible value of type `int` is a valid value for `"MaxValue"`, so we cannot jut use `-1` to represent the absence of the parameter in the config file. [endsect] -[include motivation.qbk] -[include development.qbk] -[include reference.qbk] -[include examples.qbk] -[include special_cases.qbk] -[include dependencies.qbk] -[include acknowledgments.qbk] +[section Solution] + +This is how you solve it with `boost::optional`: + + #include + + boost::optional getConfigParam(std::string name); // return either an int or a `not-an-int` + + int main() + { + if (boost::optional oi = getConfigParam("MaxValue")) // did I get a real int? + runWithMax(*oi); // use my int + else + runWithNoMax(); + } + +[endsect] +[endsect] + +[include 01_tutorial.qbk] +[include 02_discussion.qbk] +[include 03_development.qbk] +[include 04_reference.qbk] +[include 05_examples.qbk] +[include 10_optional_references.qbk] +[include 11_special_cases.qbk] +[include 90_dependencies.qbk] +[include 91_acknowledgments.qbk] diff --git a/doc/01_tutorial.qbk b/doc/01_tutorial.qbk new file mode 100644 index 0000000..cf8be16 --- /dev/null +++ b/doc/01_tutorial.qbk @@ -0,0 +1,113 @@ +[/ + Boost.Optional + + Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal + Copyright (c) 2014 Andrzej Krzemienski + + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +] + + +[section Tutorial] + +[section Optional return values] + +Let's write and use a converter function that converts an a `std::string` to an `int`. It is possible that for a given string (e.g. `"cat"`) there exist no value of type `int` capable of representing the conversion result. We do not consider such situation an error. We expect that the converter can be used only to check if the conversion is possible. A natural signature for this function can be: + + #include + boost::optionl convert(const std::string& text); + +All necessary functionality can be included with one header ``. The above function signature means that the function can either return a value of type `int` or a flag indicating that no value of `int` is available. This does not indicate an error. It is like one additional value of `int`. This is how we can use our function: + + const std::string& text = /*... */; + boost::optionl oi = convert(text); // move-construct + if (oi) // contextual conversion to bool + int i = *oi; // operator* + +In order to test if `optional` contains a value, we use the contextual conversion to type `bool`. Because of this we can combine the initialization of the optional object and the test into one instruction: + + if (boost::optionl oi = convert(text)) + int i = *oi; + +We extract the contained value with `operator*` (and with `operator->` where it makes sense). An attempt to extract the contained value of an uninitialized optional object is an ['undefined behaviour] (UB). This implementation guards the call with `BOOST_ASSERT`. Therefore you should be sure that the contained value is there before extracting. For instance, the following code is reasonably UB-safe: + + int i = *convert("100"); + +This is because we know that string value `"100"` converts to a valid value of `int`. If you do not like this potential UB, you can use an alternative way of extracting the contained value: + + try { + int j = convert(text).value(); + } + catch (const boost::bad_optional_access&) { + // deal with it + } + +This version throws an exception upon an attempt to access a non-existent contained value. If your way of dealing with the missing value is to use some default, like `0`, there exists a yet another alternative: + + int k = convert(text).value_or(0); + +This uses the `atoi`-like approach to conversions: if `text` does not represent an integral number just return `0`. Now, let's consider how function `convert` can be implemented. + + boost::optionl convert(const std::string& text) + { + std::stringstream s(text); + int i; + if ((s >> i) && s.get() == std::char_traits::eof()) + return i; + else + return boost::none; + } + +Observe the two return statements. `return i` uses the converting constructor that can create `optional` from `T`. Thus constructed optional object is initialized and its value is a copy of `i`. The other return statement uses another converting constructor from a special tag `boost::none`. It is used to indicate that we want to create an uninitialized optional object. + + +[endsect] + +[section Optional data members] +Suppose we want to implement a ['lazy load] optimization. This is because we do not want to perform an expensive initialization of our `Resource` until (if at all) it is really used. We can do it this way: + + class Widget + { + boost::optional resource_; + + public: + Widget() {} + + Resource& getResource() // not thread-safe + { + if (resource_ == boost::none) + resource_.emplace("resource", "arguments"); + + return *resource_; + } + }; + +`optional`'s default constructor creates an uninitialized optional. No call to `Resource`'s default constructor is attempted. `Resource` doesn't have to be __SGI_DEFAULT_CONSTRUCTIBLE__. In function `getResource` we first check if `resource_` is initialized. This time we do not use the contextual conversion to `bool`, but a comparison with `boost::none`. These two ways are equivalent. Function `emplace` initializes the optional in-place by perfect-forwarding the arguments to the constructor of `Resource`. No copy- or move-construction is involved here. `Resource` doesn't even have to be `MoveConstructible`. + +[note Function `emplace` is only available on compilers that support rvalue references and variadic templates. If your compiler does not support these features and you still need to avoid any move-constructions, use [link boost_optional.in_place_factories In-Place Factories].] + +[endsect] + +[section Bypassing unnecessary default construction] +Suppose we have class `Date`, which does not have a default constructor: there is no good candidate for a default date. We have a function that returns two dates in form of a `boost::tuple`: + + boost::tuple getPeriod(); + +In other place we want to use the result of `getPeriod`, but want the two dates to be named: `begin` and `end`. We want to implement something like 'multiple return values': + + Date begin, end; // Error: no default ctor! + boost::tie(begin, end) = getPeriod(); + +The second line works already, this is the capability of Boost.Tuple library, but the first line won't work. We could set some initial invented dates, but it is confusing and may be an unacceptable cost, given that these values will be overwritten in the next line anyway. This is where `optional` can help: + + boost::optional begin, end; + boost::tie(begin, end) = getPeriod(); + +It works because inside `boost::tie` a move-assignment from `T` is invoked on `optional`, which internally calls a move-constructor of `T`. +[endsect] + +[endsect] + + diff --git a/doc/motivation.qbk b/doc/02_discussion.qbk similarity index 99% rename from doc/motivation.qbk rename to doc/02_discussion.qbk index fc014c3..ee69444 100644 --- a/doc/motivation.qbk +++ b/doc/02_discussion.qbk @@ -9,7 +9,7 @@ http://www.boost.org/LICENSE_1_0.txt) ] -[section Motivation] +[section Discussion] Consider these functions which should return a value but which might not have a value to return: diff --git a/doc/development.qbk b/doc/03_development.qbk similarity index 98% rename from doc/development.qbk rename to doc/03_development.qbk index 9340407..bb89134 100644 --- a/doc/development.qbk +++ b/doc/03_development.qbk @@ -13,13 +13,13 @@ [section The models] In C++, we can ['declare] an object (a variable) of type `T`, and we can give this -variable an ['initial value] (through an ['initializer]. (c.f. 8.5)). +variable an ['initial value] (through an ['initializer]. (cf. 8.5)). When a declaration includes a non-empty initializer (an initial value is given), it is said that the object has been initialized. If the declaration uses an empty initializer (no initial value is given), and neither default nor value initialization applies, it is said that the object is [*uninitialized]. Its actual value exist but has an ['indeterminate initial value] -(c.f. 8.5.9). +(cf. 8.5/11). `optional` intends to formalize the notion of initialization (or lack of it) allowing a program to test whether an object has been initialized and stating that access to the value of an uninitialized object is undefined behavior. That @@ -38,7 +38,7 @@ additional information to tell if an object has been effectively initialized. One of the typical ways in which this has been historically dealt with is via a special value: `EOF`, `npos`, -1, etc... This is equivalent to adding the special value to the set of possible values of a given type. This super set of -`T` plus some ['nil_t]—were `nil_t` is some stateless POD-can be modeled in modern +`T` plus some ['nil_t]—where `nil_t` is some stateless POD—can be modeled in modern languages as a [*discriminated union] of T and nil_t. Discriminated unions are often called ['variants]. A variant has a ['current type], which in our case is either `T` or `nil_t`. @@ -197,7 +197,7 @@ be undefined unless the implied pointee actually exist. Such a ['de facto] idiom for referring to optional objects can be formalized in the form of a concept: the __OPTIONAL_POINTEE__ concept. This concept captures the syntactic usage of operators `*`, `->` and -conversion to `bool` to convey the notion of optionality. +contextual conversion to `bool` to convey the notion of optionality. However, pointers are good to [_refer] to optional objects, but not particularly good to handle the optional objects in all other respects, such as initializing diff --git a/doc/reference.qbk b/doc/04_reference.qbk similarity index 97% rename from doc/reference.qbk rename to doc/04_reference.qbk index cd2e354..fbeb821 100644 --- a/doc/reference.qbk +++ b/doc/04_reference.qbk @@ -58,6 +58,8 @@ template optional& operator = ( optional const& rhs ) ; ``[link reference_optional_operator_equal_other_optional __GO_TO__]`` template optional& operator = ( optional&& rhs ) ; ``[link reference_optional_operator_move_equal_other_optional __GO_TO__]`` + + template void emplace ( Args...&& args ) ; ``[link reference_optional_emplace __GO_TO__]`` template optional& operator = ( InPlaceFactory const& f ) ; ``[link reference_optional_operator_equal_factory __GO_TO__]`` @@ -746,6 +748,21 @@ assert ( *opt1 == static_cast(v) ) ; __SPACE__ +[#reference_optional_emplace] + +[: `template void optional::emplace( Args...&& args );`] + +* [*Requires:] The compiler supports rvalue references and variadic templates. +* [*Effect:] If `*this` is initialized calls `*this = none`. + Then initializes in-place the contained value as if direct-initializing an object + of type `T` with `std::forward(args)...`. +* [*Postconditions: ] `*this` is [_initialized]. +* [*Throws:] Whatever the selected `T`'s constructor throws. +* [*Notes:] `T` need not be `MoveConstructible` or `MoveAssignable`. +* [*Exception Safety:] If an exception is thrown during the initialization of `T`, `*this` is ['uninitialized]. + +__SPACE__ + [#reference_optional_operator_equal_factory] [: `template optional& optional::operator=( InPlaceFactory const& f );`] @@ -823,7 +840,7 @@ try { assert ( false ); } catch(bad_optional_access&) { - asert ( true ); + assert ( true ); } `` __SPACE__ diff --git a/doc/examples.qbk b/doc/05_examples.qbk similarity index 100% rename from doc/examples.qbk rename to doc/05_examples.qbk diff --git a/doc/10_optional_references.qbk b/doc/10_optional_references.qbk new file mode 100644 index 0000000..2f8b3c6 --- /dev/null +++ b/doc/10_optional_references.qbk @@ -0,0 +1,120 @@ + +[section Optional references] + +This library allows the template parameter `T` to be of reference type: +`T&`, and to some extent, `T const&`. + +However, since references are not real objects some restrictions apply and +some operations are not available in this case: + +* Converting constructors +* Converting assignment +* InPlace construction +* InPlace assignment +* Value-access via pointer + +Also, even though `optional` treats it wrapped pseudo-object much as +a real value, a true real reference is stored so aliasing will ocurr: + +* Copies of `optional` will copy the references but all these references +will nonetheless refer to the same object. +* Value-access will actually provide access to the referenced object +rather than the reference itself. + +[warning On compilers that do not conform to Standard C++ rules of reference binding, operations on optional references might give adverse results: rather than binding a reference to a designated object they may create an unexpected temporary and bind to it. For more details see [link boost_optional.dependencies_and_portability.optional_reference_binding Dependencies and Portability section].] + +[heading Rvalue references] + +Rvalue references and lvalue references to const have the ability in C++ to extend the life time of a temporary they bind to. Optional references do not have this capability, therefore to avoid surprising effects it is not possible to initialize an optional references from a temporary. Optional rvalue references are disabled altogether. Also, the initialization and assignment of an optional reference to const from rvalue reference is disabled. + + const int& i = 1; // legal + optional oi = 1; // illegal + +[endsect] + +[section Rebinding semantics for assignment of optional references] + +If you assign to an ['uninitialized ] `optional` the effect is to bind (for +the first time) to the object. Clearly, there is no other choice. + + int x = 1 ; + int& rx = x ; + optional ora ; + optional orb(x) ; + ora = orb ; // now 'ora' is bound to 'x' through 'rx' + *ora = 2 ; // Changes value of 'x' through 'ora' + assert(x==2); + +If you assign to a bare C++ reference, the assignment is forwarded to the +referenced object; its value changes but the reference is never rebound. + + int a = 1 ; + int& ra = a ; + int b = 2 ; + int& rb = b ; + ra = rb ; // Changes the value of 'a' to 'b' + assert(a==b); + b = 3 ; + assert(ra!=b); // 'ra' is not rebound to 'b' + +Now, if you assign to an ['initialized ] `optional`, the effect is to +[*rebind] to the new object instead of assigning the referee. This is unlike +bare C++ references. + + int a = 1 ; + int b = 2 ; + int& ra = a ; + int& rb = b ; + optional ora(ra) ; + optional orb(rb) ; + ora = orb ; // 'ora' is rebound to 'b' + *ora = 3 ; // Changes value of 'b' (not 'a') + assert(a==1); + assert(b==3); + +[heading Rationale] + +Rebinding semantics for the assignment of ['initialized ] `optional` references has +been chosen to provide [*consistency among initialization states] even at the +expense of lack of consistency with the semantics of bare C++ references. +It is true that `optional` strives to behave as much as possible as `U` +does whenever it is initialized; but in the case when `U` is `T&`, doing so would +result in inconsistent behavior w.r.t to the lvalue initialization state. + +Imagine `optional` forwarding assignment to the referenced object (thus +changing the referenced object value but not rebinding), and consider the +following code: + + optional a = get(); + int x = 1 ; + int& rx = x ; + optional b(rx); + a = b ; + +What does the assignment do? + +If `a` is ['uninitialized], the answer is clear: it binds to `x` (we now have +another reference to `x`). +But what if `a` is already ['initialized]? it would change the value of the +referenced object (whatever that is); which is inconsistent with the other +possible case. + +If `optional` would assign just like `T&` does, you would never be able to +use Optional's assignment without explicitly handling the previous +initialization state unless your code is capable of functioning whether +after the assignment, `a` aliases the same object as `b` or not. + +That is, you would have to discriminate in order to be consistent. + +If in your code rebinding to another object is not an option, then it is very +likely that binding for the first time isn't either. In such case, assignment +to an ['uninitialized ] `optional` shall be prohibited. It is quite possible +that in such a scenario it is a precondition that the lvalue must be already +initialized. If it isn't, then binding for the first time is OK +while rebinding is not which is IMO very unlikely. +In such a scenario, you can assign the value itself directly, as in: + + assert(!!opt); + *opt=value; + +[endsect] diff --git a/doc/special_cases.qbk b/doc/11_special_cases.qbk similarity index 63% rename from doc/special_cases.qbk rename to doc/11_special_cases.qbk index 6173c16..110ef78 100644 --- a/doc/special_cases.qbk +++ b/doc/11_special_cases.qbk @@ -1,124 +1,4 @@  -[section Optional references] - -This library allows the template parameter `T` to be of reference type: -`T&`, and to some extent, `T const&`. - -However, since references are not real objects some restrictions apply and -some operations are not available in this case: - -* Converting constructors -* Converting assignment -* InPlace construction -* InPlace assignment -* Value-access via pointer - -Also, even though `optional` treats it wrapped pseudo-object much as -a real value, a true real reference is stored so aliasing will ocurr: - -* Copies of `optional` will copy the references but all these references -will nonetheless refer to the same object. -* Value-access will actually provide access to the referenced object -rather than the reference itself. - -[warning On compilers that do not conform to Standard C++ rules of reference binding, operations on optional references might give adverse results: rather than binding a reference to a designated object they may create an unexpected temporary and bind to it. For more details see [link boost_optional.dependencies_and_portability.optional_reference_binding Dependencies and Portability section].] - -[heading Rvalue references] - -Rvalue references and lvalue references to const have the ability in C++ to extend the life time of a temporary they bind to. Optional references do not have this capability, therefore to avoid surprising effects it is not possible to initialize an optional references from a temporary. Optional rvalue references are disabled altogether. Also, the initialization and assignment of an optional reference to const from rvalue reference is disabled. - - const int& i = 1; // legal - optional oi = 1; // illegal - -[endsect] - -[section Rebinding semantics for assignment of optional references] - -If you assign to an ['uninitialized ] `optional` the effect is to bind (for -the first time) to the object. Clearly, there is no other choice. - - int x = 1 ; - int& rx = x ; - optional ora ; - optional orb(x) ; - ora = orb ; // now 'ora' is bound to 'x' through 'rx' - *ora = 2 ; // Changes value of 'x' through 'ora' - assert(x==2); - -If you assign to a bare C++ reference, the assignment is forwarded to the -referenced object; its value changes but the reference is never rebound. - - int a = 1 ; - int& ra = a ; - int b = 2 ; - int& rb = b ; - ra = rb ; // Changes the value of 'a' to 'b' - assert(a==b); - b = 3 ; - assert(ra!=b); // 'ra' is not rebound to 'b' - -Now, if you assign to an ['initialized ] `optional`, the effect is to -[*rebind] to the new object instead of assigning the referee. This is unlike -bare C++ references. - - int a = 1 ; - int b = 2 ; - int& ra = a ; - int& rb = b ; - optional ora(ra) ; - optional orb(rb) ; - ora = orb ; // 'ora' is rebound to 'b' - *ora = 3 ; // Changes value of 'b' (not 'a') - assert(a==1); - assert(b==3); - -[heading Rationale] - -Rebinding semantics for the assignment of ['initialized ] `optional` references has -been chosen to provide [*consistency among initialization states] even at the -expense of lack of consistency with the semantics of bare C++ references. -It is true that `optional` strives to behave as much as possible as `U` -does whenever it is initialized; but in the case when `U` is `T&`, doing so would -result in inconsistent behavior w.r.t to the lvalue initialization state. - -Imagine `optional` forwarding assignment to the referenced object (thus -changing the referenced object value but not rebinding), and consider the -following code: - - optional a = get(); - int x = 1 ; - int& rx = x ; - optional b(rx); - a = b ; - -What does the assignment do? - -If `a` is ['uninitialized], the answer is clear: it binds to `x` (we now have -another reference to `x`). -But what if `a` is already ['initialized]? it would change the value of the -referenced object (whatever that is); which is inconsistent with the other -possible case. - -If `optional` would assign just like `T&` does, you would never be able to -use Optional's assignment without explicitly handling the previous -initialization state unless your code is capable of functioning whether -after the assignment, `a` aliases the same object as `b` or not. - -That is, you would have to discriminate in order to be consistent. - -If in your code rebinding to another object is not an option, then it is very -likely that binding for the first time isn't either. In such case, assignment -to an ['uninitialized ] `optional` shall be prohibited. It is quite possible -that in such a scenario it is a precondition that the lvalue must be already -initialized. If it isn't, then binding for the first time is OK -while rebinding is not which is IMO very unlikely. -In such a scenario, you can assign the value itself directly, as in: - - assert(!!opt); - *opt=value; - -[endsect] - [section In-Place Factories] One of the typical problems with wrappers and containers is that their @@ -356,6 +236,8 @@ Unless `T`'s constructor or assignment throws, assignments to `optional` do n This also applies to move assignments/constructors. However, move operations are made no-throw more often. +Operation `emplace` provides basic exception safety guarantee. If it throws, the optional object becomes uninitialized regardless of its initial state, and its previous contained value (if any) is destroyed. It doesn't call any assignment or move/copy constructor on `T`. + [heading Swap] Unless `swap` on optional is customized, its primary implementation forwards calls to `T`'s `swap` or move constructor (depending on the initialization state of the optional objects). Thus, if both `T`'s `swap` and move constructor never throw, `swap` on `optional` never throws. similarly, if both `T`'s `swap` and move constructor offer strong guarantee, `swap` on `optional` also offers a strong guarantee. @@ -365,8 +247,9 @@ In case `swap` on optional is customized, the call to `T`'s move constructor are [section Type requirements] -In general, `T` must be `MoveConstructible` and have a no-throw destructor. -The `MoveConstructible` requirement is not needed if [*InPlaceFactories] are used. +At the very minimum for `optional` to work with a minimum interface it is required that `T` has a publicly accessible no-throw destructor. In that case you need to initialize the optional object with function `emplace()` or use [*InPlaceFactories]. +Additionally, if `T` is `Moveable`, `optional` is also `Moveable` and can be easily initialized from an rvalue of type `T` and be passed by value. +Additionally if `T` is `Copyable`, `optional` is also `Copyable` and can be easily initialized from an lvalue of type `T`. `T` [_is not] required to be __SGI_DEFAULT_CONSTRUCTIBLE__. diff --git a/doc/dependencies.qbk b/doc/90_dependencies.qbk similarity index 100% rename from doc/dependencies.qbk rename to doc/90_dependencies.qbk diff --git a/doc/acknowledgments.qbk b/doc/91_acknowledgments.qbk similarity index 100% rename from doc/acknowledgments.qbk rename to doc/91_acknowledgments.qbk diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 95de749..5014bb2 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -17,7 +17,7 @@ path-constant images : html ; xml optional : - optional.qbk + 00_optional.qbk ; boostbook standalone diff --git a/doc/html/boost_optional/detailed_semantics.html b/doc/html/boost_optional/detailed_semantics.html index 8495d41..8374254 100644 --- a/doc/html/boost_optional/detailed_semantics.html +++ b/doc/html/boost_optional/detailed_semantics.html @@ -1080,6 +1080,42 @@ +

+ space +

+

+ template<class... Args> void optional<T + (not a ref)>::emplace( Args...&& + args ); +

+
    +
  • + Requires: The compiler supports rvalue + references and variadic templates. +
  • +
  • + Effect: If *this is initialized calls *this = + none. Then initializes in-place + the contained value as if direct-initializing an object of type T with std::forward<Args>(args).... +
  • +
  • + Postconditions: *this is initialized. +
  • +
  • + Throws: Whatever the selected T's constructor throws. +
  • +
  • + Notes: T + need not be MoveConstructible + or MoveAssignable. +
  • +
  • + Exception Safety: If an exception is thrown + during the initialization of T, + *this + is uninitialized. +
  • +

space

@@ -1232,7 +1268,7 @@ assert ( false ); } catch(bad_optional_access&) { - asert ( true ); + assert ( true ); } space diff --git a/doc/html/boost_optional/development.html b/doc/html/boost_optional/development.html index f289a04..1ff950f 100644 --- a/doc/html/boost_optional/development.html +++ b/doc/html/boost_optional/development.html @@ -6,7 +6,7 @@ - + @@ -20,7 +20,7 @@
-PrevUpHomeNext +PrevUpHomeNext

@@ -39,12 +39,12 @@ In C++, we can declare an object (a variable) of type T, and we can give this variable an initial value (through an initializer. - (c.f. 8.5)). When a declaration includes a non-empty initializer (an initial + (cf. 8.5)). When a declaration includes a non-empty initializer (an initial value is given), it is said that the object has been initialized. If the declaration uses an empty initializer (no initial value is given), and neither default nor value initialization applies, it is said that the object is uninitialized. Its actual value exist but - has an indeterminate initial value (c.f. 8.5.9). optional<T> intends + has an indeterminate initial value (cf. 8.5/11). optional<T> intends to formalize the notion of initialization (or lack of it) allowing a program to test whether an object has been initialized and stating that access to the value of an uninitialized object is undefined behavior. That is, when @@ -67,13 +67,12 @@ special value: EOF, npos, -1, etc... This is equivalent to adding the special value to the set of possible values of a given type. This super set of T plus some - nil_t—were nil_t - is some stateless POD-can be modeled in modern languages as a discriminated - union of T and nil_t. Discriminated unions are often called variants. - A variant has a current type, which in our case is either - T or nil_t. - Using the Boost.Variant - library, this model can be implemented in terms of boost::variant<T,nil_t>. + nil_t—where nil_t + is some stateless POD—can be modeled in modern languages as a discriminated union of T and nil_t. Discriminated + unions are often called variants. A variant has a current + type, which in our case is either T + or nil_t. Using the Boost.Variant library, this model + can be implemented in terms of boost::variant<T,nil_t>. There is precedent for a discriminated union as a model for an optional value: the Haskell Maybe built-in type constructor. Thus, a discriminated union T+nil_t @@ -329,8 +328,8 @@ Such a de facto idiom for referring to optional objects can be formalized in the form of a concept: the OptionalPointee concept. This concept captures the syntactic usage of operators *, -> - and conversion to bool to convey - the notion of optionality. + and contextual conversion to bool + to convey the notion of optionality.

However, pointers are good to refer @@ -407,7 +406,7 @@


-PrevUpHomeNext +PrevUpHomeNext
diff --git a/doc/html/boost_optional/discussion.html b/doc/html/boost_optional/discussion.html new file mode 100644 index 0000000..5eb1c93 --- /dev/null +++ b/doc/html/boost_optional/discussion.html @@ -0,0 +1,128 @@ + + + +Discussion + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+ +

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

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

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

+

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

+

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

+

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

+

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

+

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

+

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

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

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

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

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

+

+ Clearly, we need a better idiom. +

+
+ + + +
+
+
+PrevUpHomeNext +
+ + diff --git a/doc/html/boost_optional/exception_safety_guarantees.html b/doc/html/boost_optional/exception_safety_guarantees.html index 4b2b0a5..2537918 100644 --- a/doc/html/boost_optional/exception_safety_guarantees.html +++ b/doc/html/boost_optional/exception_safety_guarantees.html @@ -118,6 +118,13 @@ This also applies to move assignments/constructors. However, move operations are made no-throw more often.

+

+ Operation emplace provides + basic exception safety guarantee. If it throws, the optional object becomes + uninitialized regardless of its initial state, and its previous contained value + (if any) is destroyed. It doesn't call any assignment or move/copy constructor + on T. +

Swap diff --git a/doc/html/boost_optional/synopsis.html b/doc/html/boost_optional/synopsis.html index 8a22968..b350ded 100644 --- a/doc/html/boost_optional/synopsis.html +++ b/doc/html/boost_optional/synopsis.html @@ -74,6 +74,8 @@ template<class U> optional& operator = ( optional<U>&& rhs ) ; R + template<class... Args> void emplace ( Args...&& args ) ; R + template<class InPlaceFactory> optional& operator = ( InPlaceFactory const& f ) ; R template<class TypedInPlaceFactory> optional& operator = ( TypedInPlaceFactory const& f ) ; R diff --git a/doc/html/boost_optional/tutorial.html b/doc/html/boost_optional/tutorial.html new file mode 100644 index 0000000..c1b6173 --- /dev/null +++ b/doc/html/boost_optional/tutorial.html @@ -0,0 +1,239 @@ + + + +Tutorial + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+ + +
+ +

+ Let's write and use a converter function that converts an a std::string + to an int. It is possible that + for a given string (e.g. "cat") + there exist no value of type int + capable of representing the conversion result. We do not consider such situation + an error. We expect that the converter can be used only to check if the conversion + is possible. A natural signature for this function can be: +

+
#include <boost/optional.hpp>
+boost::optionl<int> convert(const std::string& text);
+
+

+ All necessary functionality can be included with one header <boost/optional.hpp>. + The above function signature means that the function can either return a + value of type int or a flag + indicating that no value of int + is available. This does not indicate an error. It is like one additional + value of int. This is how we + can use our function: +

+
const std::string& text = /*... */;
+boost::optionl<int> oi = convert(text); // move-construct
+if (oi)                                 // contextual conversion to bool
+  int i = *oi;                          // operator*
+
+

+ In order to test if optional + contains a value, we use the contextual conversion to type bool. Because of this we can combine the initialization + of the optional object and the test into one instruction: +

+
if (boost::optionl<int> oi = convert(text))
+  int i = *oi;
+
+

+ We extract the contained value with operator* (and with operator-> where it makes sense). An attempt to + extract the contained value of an uninitialized optional object is an undefined + behaviour (UB). This implementation guards the call with BOOST_ASSERT. Therefore you should be sure + that the contained value is there before extracting. For instance, the following + code is reasonably UB-safe: +

+
int i = *convert("100");
+
+

+ This is because we know that string value "100" + converts to a valid value of int. + If you do not like this potential UB, you can use an alternative way of extracting + the contained value: +

+
try {
+  int j = convert(text).value();
+}
+catch (const boost::bad_optional_access&) {
+  // deal with it
+}
+
+

+ This version throws an exception upon an attempt to access a non-existent + contained value. If your way of dealing with the missing value is to use + some default, like 0, there exists + a yet another alternative: +

+
int k = convert(text).value_or(0);
+
+

+ This uses the atoi-like approach + to conversions: if text does + not represent an integral number just return 0. + Now, let's consider how function convert + can be implemented. +

+
boost::optionl<int> convert(const std::string& text)
+{
+  std::stringstream s(text);
+  int i;
+  if ((s >> i) && s.get() == std::char_traits<char>::eof())
+    return i;
+  else
+    return boost::none;
+}
+
+

+ Observe the two return statements. return + i uses the converting constructor + that can create optional<T> + from T. Thus constructed + optional object is initialized and its value is a copy of i. + The other return statement uses another converting constructor from a special + tag boost::none. It is used to indicate that we want + to create an uninitialized optional object. +

+
+
+ +

+ Suppose we want to implement a lazy load optimization. + This is because we do not want to perform an expensive initialization of + our Resource until (if at + all) it is really used. We can do it this way: +

+
class Widget
+{
+  boost::optional<Resource> resource_;
+
+public:
+  Widget() {}
+
+  Resource& getResource() // not thread-safe
+  {
+    if (resource_ == boost::none)
+        resource_.emplace("resource", "arguments");
+
+    return *resource_;
+  }
+};
+
+

+ optional's default constructor + creates an uninitialized optional. No call to Resource's + default constructor is attempted. Resource + doesn't have to be Default + Constructible. In function getResource + we first check if resource_ + is initialized. This time we do not use the contextual conversion to bool, but a comparison with boost::none. These two ways are equivalent. Function + emplace initializes the optional + in-place by perfect-forwarding the arguments to the constructor of Resource. No copy- or move-construction + is involved here. Resource + doesn't even have to be MoveConstructible. +

+
+ + + + + +
[Note]Note

+ Function emplace is only + available on compilers that support rvalue references and variadic templates. + If your compiler does not support these features and you still need to + avoid any move-constructions, use In-Place + Factories. +

+
+
+ +

+ Suppose we have class Date, + which does not have a default constructor: there is no good candidate for + a default date. We have a function that returns two dates in form of a boost::tuple: +

+
boost::tuple<Date, Date> getPeriod();
+
+

+ In other place we want to use the result of getPeriod, + but want the two dates to be named: begin + and end. We want to implement + something like 'multiple return values': +

+
Date begin, end; // Error: no default ctor!
+boost::tie(begin, end) = getPeriod();
+
+

+ The second line works already, this is the capability of Boost.Tuple library, + but the first line won't work. We could set some initial invented dates, + but it is confusing and may be an unacceptable cost, given that these values + will be overwritten in the next line anyway. This is where optional can help: +

+
boost::optional<Date> begin, end;
+boost::tie(begin, end) = getPeriod();
+
+

+ It works because inside boost::tie a + move-assignment from T is + invoked on optional<T>, + which internally calls a move-constructor of T. +

+
+
+ + + +
+
+
+PrevUpHomeNext +
+ + diff --git a/doc/html/boost_optional/type_requirements.html b/doc/html/boost_optional/type_requirements.html index 6c68e88..8ffd2ea 100644 --- a/doc/html/boost_optional/type_requirements.html +++ b/doc/html/boost_optional/type_requirements.html @@ -27,9 +27,17 @@ Type requirements

- In general, T must be MoveConstructible and have a no-throw destructor. - The MoveConstructible requirement - is not needed if InPlaceFactories are used. + At the very minimum for optional<T> + to work with a minimum interface it is required that T + has a publicly accessible no-throw destructor. In that case you need to initialize + the optional object with function emplace() or use InPlaceFactories. + Additionally, if T is Moveable, optional<T> + is also Moveable and can be + easily initialized from an rvalue of type T + and be passed by value. Additionally if T + is Copyable, optional<T> is + also Copyable and can be easily + initialized from an lvalue of type T.

T is diff --git a/doc/html/index.html b/doc/html/index.html index 9854163..4f602c2 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -5,7 +5,7 @@ - + @@ -17,7 +17,7 @@
More


-
Next
+
Next

@@ -38,7 +38,20 @@

Table of Contents

Introduction
-
Motivation
+
+
Problem
+
Solution
+
+
Tutorial
+
+
Optional + return values
+
Optional + data members
+
Bypassing + unnecessary default construction
+
+
Discussion
Development
The models
@@ -78,24 +91,60 @@ +

- This library can be used to represent 'optional' (or 'nullable') objects that - can be safely passed by value: + Class template optional is + a wrapper for representing 'optional' (or 'nullable') objects who may not (yet) + contain a valid value. Optional objects offer full value semantics; they are + good for passing by value and usage inside STL containers. This is a header-only + library.

-
optional<int> readInt(); // this function may return either an int or a not-an-int
+
+ +

+ Suppose we want to read a parameter form a config file which represents some + integral value, let's call it "MaxValue". + It is possible that this parameter is not specified; such situation is no + error. It is valid to not specify the parameter and in that case the program + is supposed to behave slightly different. Also suppose that any possible + value of type int is a valid + value for "MaxValue", + so we cannot jut use -1 + to represent the absence of the parameter in the config file. +

+
+
+ +

+ This is how you solve it with boost::optional: +

+
#include <boost/optional.hpp>
 
-if (optional<int> oi = readInt()) // did I get a real int
-  cout << "my int is: " << *oi;   // use my int
-else
-  cout << "I have no int";
+boost::optional<int> getConfigParam(std::string name);  // return either an int or a `not-an-int`
+
+int main()
+{
+  if (boost::optional<int> oi = getConfigParam("MaxValue")) // did I get a real int?
+    runWithMax(*oi);                                        // use my int
+  else
+    runWithNoMax();
+}
 

+
- +

Last revised: May 23, 2014 at 14:35:08 GMT

Last revised: June 03, 2014 at 14:35:30 GMT


-
Next
+
Next
diff --git a/include/boost/detail/none_t.hpp b/include/boost/detail/none_t.hpp deleted file mode 100644 index 40805ec..0000000 --- a/include/boost/detail/none_t.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2003, Fernando Luis Cacciola Carballal. -// -// Use, modification, and distribution is subject to the Boost Software -// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// See http://www.boost.org/libs/optional for documentation. -// -// You are welcome to contact the author at: -// fernando_cacciola@hotmail.com -// -#ifndef BOOST_DETAIL_NONE_T_17SEP2003_HPP -#define BOOST_DETAIL_NONE_T_17SEP2003_HPP - -namespace boost { - -namespace detail { - -struct none_helper{}; - -typedef int none_helper::*none_t ; - -} // namespace detail - -} // namespace boost - -#endif - diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 5a9cc46..4a8dc5e 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -460,14 +460,26 @@ class optional_base : public optional_tag void construct ( argument_type val ) { - new (m_storage.address()) internal_type(val) ; + ::new (m_storage.address()) internal_type(val) ; m_initialized = true ; } #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES void construct ( rval_reference_type val ) { - new (m_storage.address()) internal_type( types::move(val) ) ; + ::new (m_storage.address()) internal_type( types::move(val) ) ; + m_initialized = true ; + } +#endif + +#if (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES) + // Constructs in-place + // upon exception *this is always uninitialized + template + void emplace_assign ( Args&&... args ) + { + destroy(); + ::new (m_storage.address()) internal_type( boost::forward(args)... ); m_initialized = true ; } #endif @@ -507,6 +519,7 @@ class optional_base : public optional_tag destroy(); construct(factory,tag); } + #else // Constructs in-place using the given factory template @@ -907,6 +920,16 @@ class optional : public optional_detail::optional_base this->assign( none_ ) ; return *this ; } + +#if (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES) + // Constructs in-place + // upon exception *this is always uninitialized + template + void emplace ( Args&&... args ) + { + this->emplace_assign( boost::forward(args)... ); + } +#endif void swap( optional & arg ) BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 10fd597..48ddf3f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -24,6 +24,7 @@ import testing ; [ run optional_test_move.cpp ] [ run optional_test_equals_none.cpp ] [ run optional_test_value_access.cpp ] + [ run optional_test_emplace.cpp ] [ compile-fail optional_test_fail1.cpp ] [ compile-fail optional_test_fail3a.cpp ] [ compile-fail optional_test_fail3b.cpp ] diff --git a/test/optional_test_emplace.cpp b/test/optional_test_emplace.cpp new file mode 100644 index 0000000..2bc761a --- /dev/null +++ b/test/optional_test_emplace.cpp @@ -0,0 +1,169 @@ +// Copyright (C) 2014 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com +// +// Revisions: +// +#include +#include +#include + +#define BOOST_ENABLE_ASSERT_HANDLER + +#include "boost/bind/apply.hpp" // Included just to test proper interaction with boost::apply<> as reported by Daniel Wallin +#include "boost/mpl/bool.hpp" +#include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_ +#include "boost/static_assert.hpp" + +#include "boost/optional/optional.hpp" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#include "boost/none.hpp" + +#include "boost/test/minimal.hpp" + +#include "optional_test_common.cpp" + +//#ifndef BOOST_OPTIONAL_NO_CONVERTING_ASSIGNMENT +//#ifndef BOOST_OPTIONAL_NO_CONVERTING_COPY_CTOR + +#if (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES) + +class Guard +{ +public: + int which_ctor; + Guard () : which_ctor(0) { } + Guard (int&, double&&) : which_ctor(1) { } + Guard (int&&, double&) : which_ctor(2) { } + Guard (int&&, double&&) : which_ctor(3) { } + Guard (int&, double&) : which_ctor(4) { } + Guard (std::string const&) : which_ctor(5) { } + Guard (std::string &) : which_ctor(6) { } + Guard (std::string &&) : which_ctor(7) { } +private: + Guard(Guard&&); + Guard(Guard const&); + void operator=(Guard &&); + void operator=(Guard const&); +}; + +struct Thrower +{ + Thrower(bool throw_) { if (throw_) throw int(); } + +private: + Thrower(Thrower const&); + Thrower(Thrower&&); +}; + +struct ThrowOnMove +{ + ThrowOnMove(ThrowOnMove&&) { throw int(); } + ThrowOnMove(ThrowOnMove const&) { throw int(); } + ThrowOnMove(int){} +}; + +void test_emplace() +{ + int i = 0; + double d = 0.0; + const std::string cs; + std::string ms; + optional o; + + o.emplace(); + BOOST_CHECK(o); + BOOST_CHECK(0 == o->which_ctor); + + o.emplace(i, 2.0); + BOOST_CHECK(o); + BOOST_CHECK(1 == o->which_ctor); + + o.emplace(1, d); + BOOST_CHECK(o); + BOOST_CHECK(2 == o->which_ctor); + + o.emplace(1, 2.0); + BOOST_CHECK(o); + BOOST_CHECK(3 == o->which_ctor); + + o.emplace(i, d); + BOOST_CHECK(o); + BOOST_CHECK(4 == o->which_ctor); + + o.emplace(cs); + BOOST_CHECK(o); + BOOST_CHECK(5 == o->which_ctor); + + o.emplace(ms); + BOOST_CHECK(o); + BOOST_CHECK(6 == o->which_ctor); + + o.emplace(std::string()); + BOOST_CHECK(o); + BOOST_CHECK(7 == o->which_ctor); +} + +void test_clear_on_throw() +{ + optional ot; + try { + ot.emplace(false); + BOOST_CHECK(ot); + } catch(...) { + BOOST_CHECK(false); + } + + try { + ot.emplace(true); + BOOST_CHECK(false); + } catch(...) { + BOOST_CHECK(!ot); + } +} + +void test_no_moves_on_emplacement() +{ + try { + optional o; + o.emplace(1); + BOOST_CHECK(o); + } + catch (...) { + BOOST_CHECK(false); + } +} + +#endif + + +int test_main( int, char* [] ) +{ + try + { +#if (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES) + test_emplace(); + test_clear_on_throw(); + test_no_moves_on_emplacement(); +#endif + } + catch ( ... ) + { + BOOST_ERROR("Unexpected Exception caught!"); + } + + return 0; +} + +