From befd3970d7b3b7dae49afeb5bb7630f599ed5241 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sun, 8 Jun 2014 16:23:35 +0200 Subject: [PATCH] docs -- expanded tutprial section --- doc/00_optional.qbk | 9 +- doc/01_quick_start.qbk | 4 +- doc/{12_motivation.qbk => 10_motivation.qbk} | 0 ...{13_development.qbk => 11_development.qbk} | 0 doc/12_when_to_use.qbk | 37 +++++ doc/13_relational_operators.qbk | 35 +++++ ...sing_unnecessary_default_construction.html | 8 +- .../quick_start/storage_in_containers.html | 11 +- .../design_overview/the_interface.html | 6 +- .../tutorial/optional_references.html | 6 +- .../tutorial/relational_operators.html | 108 +++++++++++++ .../tutorial/when_to_use_optional.html | 142 ++++++++++++++++++ doc/html/index.html | 6 +- doc/html/optional/tutorial.html | 4 + 14 files changed, 353 insertions(+), 23 deletions(-) rename doc/{12_motivation.qbk => 10_motivation.qbk} (100%) rename doc/{13_development.qbk => 11_development.qbk} (100%) create mode 100644 doc/12_when_to_use.qbk create mode 100644 doc/13_relational_operators.qbk create mode 100644 doc/html/boost_optional/tutorial/relational_operators.html create mode 100644 doc/html/boost_optional/tutorial/when_to_use_optional.html diff --git a/doc/00_optional.qbk b/doc/00_optional.qbk index 96209ef..fb5d584 100644 --- a/doc/00_optional.qbk +++ b/doc/00_optional.qbk @@ -23,6 +23,7 @@ Distributed under the Boost Software License, Version 1.0. [/ Cited Boost resources ] [def __BOOST_VARIANT__ [@../../../variant/index.html Boost.Variant]] +[def __BOOST_TUPLE__ [@../../../tuple/index.html Boost.Tuple]] [def __BOOST_TRIBOOL__ [@../../../../doc/html/tribool.html boost::tribool]] [def __OPTIONAL_POINTEE__ [@../../../utility/OptionalPointee.html OptionalPointee]] @@ -37,6 +38,8 @@ Distributed under the Boost Software License, Version 1.0. [def __HASKELL__ [@http://www.haskell.org/ Haskell]] [def __SGI_DEFAULT_CONSTRUCTIBLE__ [@http://www.sgi.com/tech/stl/DefaultConstructible.html `DefaultConstructible`]] +[def __SGI_LESS_THAN_COMPARABLE__ [@http://www.sgi.com/tech/stl/LessThanComparable.html `LessThanComparable`]] +[def __SGI_EQUALITY_COMPARABLE__ [@http://www.sgi.com/tech/stl/EqualityComparable.html `EqualityComparable`]] [/ Icons ] @@ -71,8 +74,10 @@ This is how you solve it with `boost::optional`: [include 01_quick_start.qbk] [section Tutorial] -[include 12_motivation.qbk] -[include 13_development.qbk] +[include 10_motivation.qbk] +[include 11_development.qbk] +[include 12_when_to_use.qbk] +[include 13_relational_operators.qbk] [include 14_optional_references.qbk] [include 15_in_place_factories.qbk] [include 16_optional_bool.qbk] diff --git a/doc/01_quick_start.qbk b/doc/01_quick_start.qbk index fee59c5..10d55e6 100644 --- a/doc/01_quick_start.qbk +++ b/doc/01_quick_start.qbk @@ -119,7 +119,7 @@ In other place we want to use the result of `getPeriod`, but want the two dates 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 invented initial 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: +The second line works already, this is the capability of __BOOST_TUPLE__ library, but the first line won't work. We could set some invented initial 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(); @@ -138,7 +138,7 @@ Suppose you want to ask users to choose some number (an `int`). One of the valid ++choices[choice]; } -This works because `optional` is `LessThanComparable` whenever `T` is `LessThanComparable`. In this case the state of being uninitialized is treated as a yet another value of `T`, which is compared less than any value of `T`. So the set of values that type `optional` can assume is {`boost::none`, -2147483648, -2147483647, ..., -1, 0, 1, ..., 2147483647} (assuming a 32-bit `int`). +This works because `optional` is __SGI_LESS_THAN_COMPARABLE__ whenever `T` is __SGI_LESS_THAN_COMPARABLE__. In this case the state of being uninitialized is treated as a yet another value of `T`, which is compared less than any value of `T`. [endsect] [endsect] diff --git a/doc/12_motivation.qbk b/doc/10_motivation.qbk similarity index 100% rename from doc/12_motivation.qbk rename to doc/10_motivation.qbk diff --git a/doc/13_development.qbk b/doc/11_development.qbk similarity index 100% rename from doc/13_development.qbk rename to doc/11_development.qbk diff --git a/doc/12_when_to_use.qbk b/doc/12_when_to_use.qbk new file mode 100644 index 0000000..80dff54 --- /dev/null +++ b/doc/12_when_to_use.qbk @@ -0,0 +1,37 @@ + +[section When to use Optional] + +It is recommended to use `optional` in situations where there is exactly one, clear (to all parties) reason for having no value of type `T`, and where the lack of value is as natural as having any regular value of `T`. One example of such situation is asking the user in some GUI form to optionally specify some limit on an `int` value, but the user is allowed to say 'I want the number not to be constrained by the maximum'. +For another example, consider a config parameter specifying how many threads the application should launch. Leaving this parameter unspecified means that the application should decide itself. For yet another example, consider a function returning the index of the smallest element in a `vector`. We need to be prepared for the situation, where the `vector` is empty. Therefore a natural signature for such function would be: + + template + optional find_smallest_elem(const std::vector& vec); + +Here, having received an empty `vec` and having no `size_t` to return is not a ['failure] but a ['normal], albeit irregular, situation. + +Another typical situation is to indicate that we do not have a value yet, but we expect to have it later. This notion can be used in implementing solutions like lazy initialization or a two-phase initialization. + +`optional` can be used to take a non-__SGI_DEFAULT_CONSTRUCTIBLE__ type `T` and create a sibling type with a default constructor. This is a way to add a ['null-state] to any type that doesn't have it already. + +Sometimes type `T` already provides a built-in null-state, but it may still be useful to wrap it into `optional`. Consider `std::string`. When you read a piece of text from a GUI form or a DB table, it is hardly ever that the empty string indicates anything else but a missing text. And some data bases do not even distinguish between a null string entry and a non-null string of length 0. Still, it may be practical to use `optional` to indicate in the returned type that we want to treat the empty string in a special dedicated program path: + + if(boost::optional name = ask_user_name()) { + assert(*name != ""); + logon_as(*name); + } + else { + skip_logon(); + } + +In the example above, the assertion indicates that if we choose to use this technique, we must translate the empty string state to an optional object with no contained value (inside function `ask_user_name`). + +[heading Not recommended usages] + +It is not recommended to use `optional` to indicate that we were not able to compute a value because of a ['failure]. It is difficult to define what a failure is, but it usually has one common characteristic: an associated information on the cause of the failure. This can be the type and member data of an exception object, or an error code. It is a bad design to signal a failure and not inform about the cause. If you do not want to use exceptions, and do not like the fact that by returning error codes you cannot return the computed value, you can use [@https://github.com/ptal/Boost.Expected Expected] library. It is sort of __BOOST_VARIANT__ that contains either a computed value or a reason why the computation failed. + +Sometimes the distinction into what is a failure and what is a valid but irregular result is blurry and depends on a particular usage and personal preference. Consider a function that converts a `string` to an `int`. Is it a failure that you cannot convert? It might in some cases, but in other you may call it exactly for the purpose of figuring out if a given `string` is convertible, and you are not even interested in the resulting value. Sometimes when a conversion fails you may not consider it a failure, but you need to know why it cannot be converted; for instance at which character it is determined that the conversion is impossible. In this case returning `optional` will not suffice. Finally, there is a use case where an input string that does not represent an `int` is not a failure condition, but during the conversion we use resources whose acquisition may fail. In that case the natural representation is to both return `optional` and signal failure: + + optional convert1(const string& str); // throws + expected> convert2(const string& str); // return either optional or error + +[endsect] diff --git a/doc/13_relational_operators.qbk b/doc/13_relational_operators.qbk new file mode 100644 index 0000000..2797c27 --- /dev/null +++ b/doc/13_relational_operators.qbk @@ -0,0 +1,35 @@ + +[section Relational operators] + +Type `optional` is __SGI_EQUALITY_COMPARABLE__ whenever `T` is __SGI_EQUALITY_COMPARABLE__. Two optional objects containing a value compare in the same as their contained values. The uninitialized state of `optional` is treated as a distinct value, equal to itself, and unequal to any value of type `T`: + + boost::optional oN = boost::none; + boost::optional o0 = 0; + boost::optional o1 = 1; + + assert(oN != o0); + assert(o1 != oN); + assert(o2 != o1); + assert(oN == oN); + assert(o0 == o0); + +The converting constructor from `T` as well as from `boost::none` implies the existence and semantics of the mixed comparison between `T` and `optional` as well as between `none_t` and `optionl`: + + assert(oN != 0); + assert(o1 != boost::none); + assert(o2 != 1); + assert(oN == boost::none); + assert(o0 == 0); + +This mixed comparison has a practical interpretation, which is occasionally useful: + + boost::optional choice = ask_user(); + if (choice == 2) + start_procedure_2(); + +In the above example, the meaning of the comparison is 'user chose number 2'. If user chose nothing, he didn't choose number 2. + +In a similar manner, type `optional` is __SGI_LESS_THAN_COMPARABLE__ whenever `T` is __SGI_LESS_THAN_COMPARABLE__. The optional object containing no value is compared less than any value of `T`. To illustrate this, if the default ordering of `size_t` is {`0`, `1`, `2`, ...}, the default ordering of `optional` is {`boost::none`, `0`, `1`, `2`, ...}. This order does not have a practical interpretation. The goal is to have any semantically correct default ordering in order for `optional` to be usable in ordered associative containers (wherever `T` is usable). + +Mixed relational operators are the only case where the contained value of an optional object can be inspected without the usage of value accessing function (`operator*`, `value`, `value_or`). +[endsect] diff --git a/doc/html/boost_optional/quick_start/bypassing_unnecessary_default_construction.html b/doc/html/boost_optional/quick_start/bypassing_unnecessary_default_construction.html index 844742b..ed964cf 100644 --- a/doc/html/boost_optional/quick_start/bypassing_unnecessary_default_construction.html +++ b/doc/html/boost_optional/quick_start/bypassing_unnecessary_default_construction.html @@ -44,10 +44,10 @@ 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 invented initial 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: + The second line works already, this is the capability of Boost.Tuple + library, but the first line won't work. We could set some invented initial + 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();
diff --git a/doc/html/boost_optional/quick_start/storage_in_containers.html b/doc/html/boost_optional/quick_start/storage_in_containers.html
index 1715431..f13f5a7 100644
--- a/doc/html/boost_optional/quick_start/storage_in_containers.html
+++ b/doc/html/boost_optional/quick_start/storage_in_containers.html
@@ -43,14 +43,9 @@
 

This works because optional<T> - is LessThanComparable whenever - T is LessThanComparable. - In this case the state of being uninitialized is treated as a yet another - value of T, which is compared - less than any value of T. - So the set of values that type optional<T> - can assume is {boost::none, -2147483648, -2147483647, ..., -1, - 0, 1, ..., 2147483647} (assuming a 32-bit int). + is LessThanComparable whenever T is LessThanComparable. In this case + the state of being uninitialized is treated as a yet another value of T, which is compared less than any value + of T.

diff --git a/doc/html/boost_optional/tutorial/design_overview/the_interface.html b/doc/html/boost_optional/tutorial/design_overview/the_interface.html index e0c3a31..8fa2707 100644 --- a/doc/html/boost_optional/tutorial/design_overview/the_interface.html +++ b/doc/html/boost_optional/tutorial/design_overview/the_interface.html @@ -7,7 +7,7 @@ - +
@@ -20,7 +20,7 @@

-PrevUpHomeNext +PrevUpHomeNext

@@ -180,7 +180,7 @@
-PrevUpHomeNext +PrevUpHomeNext
diff --git a/doc/html/boost_optional/tutorial/optional_references.html b/doc/html/boost_optional/tutorial/optional_references.html index 0ca70b6..4e067c2 100644 --- a/doc/html/boost_optional/tutorial/optional_references.html +++ b/doc/html/boost_optional/tutorial/optional_references.html @@ -6,7 +6,7 @@ - + @@ -20,7 +20,7 @@
-PrevUpHomeNext +PrevUpHomeNext

@@ -109,7 +109,7 @@
-PrevUpHomeNext +PrevUpHomeNext
diff --git a/doc/html/boost_optional/tutorial/relational_operators.html b/doc/html/boost_optional/tutorial/relational_operators.html new file mode 100644 index 0000000..be20e45 --- /dev/null +++ b/doc/html/boost_optional/tutorial/relational_operators.html @@ -0,0 +1,108 @@ + + + +Relational operators + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+ +

+ Type optional<T> is + EqualityComparable whenever T is EqualityComparable. Two optional + objects containing a value compare in the same as their contained values. + The uninitialized state of optional<T> + is treated as a distinct value, equal to itself, and unequal to any value + of type T: +

+
boost::optional<int> oN = boost::none;
+boost::optional<int> o0 = 0;
+boost::optional<int> o1 = 1;
+
+assert(oN != o0);
+assert(o1 != oN);
+assert(o2 != o1);
+assert(oN == oN);
+assert(o0 == o0);
+
+

+ The converting constructor from T + as well as from boost::none implies the existence and semantics + of the mixed comparison between T + and optional<T> as + well as between none_t and + optionl<T>: +

+
assert(oN != 0);
+assert(o1 != boost::none);
+assert(o2 != 1);
+assert(oN == boost::none);
+assert(o0 == 0);
+
+

+ This mixed comparison has a practical interpretation, which is occasionally + useful: +

+
boost::optional<int> choice = ask_user();
+if (choice == 2)
+    start_procedure_2();
+
+

+ In the above example, the meaning of the comparison is 'user chose number + 2'. If user chose nothing, he didn't choose number 2. +

+

+ In a similar manner, type optional<T> + is LessThanComparable whenever T is LessThanComparable. The optional + object containing no value is compared less than any value of T. To illustrate this, if the default ordering + of size_t is {0, 1, + 2, ...}, the default ordering + of optional<size_t> + is {boost::none, 0, + 1, 2, + ...}. This order does not have a practical interpretation. The goal is to + have any semantically correct default ordering in order for optional<T> to + be usable in ordered associative containers (wherever T + is usable). +

+

+ Mixed relational operators are the only case where the contained value of + an optional object can be inspected without the usage of value accessing + function (operator*, + value, value_or). +

+
+ + + +
+
+
+PrevUpHomeNext +
+ + diff --git a/doc/html/boost_optional/tutorial/when_to_use_optional.html b/doc/html/boost_optional/tutorial/when_to_use_optional.html new file mode 100644 index 0000000..5883446 --- /dev/null +++ b/doc/html/boost_optional/tutorial/when_to_use_optional.html @@ -0,0 +1,142 @@ + + + +When to use Optional + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+ +

+ It is recommended to use optional<T> + in situations where there is exactly one, clear (to all parties) reason for + having no value of type T, + and where the lack of value is as natural as having any regular value of + T. One example of such situation + is asking the user in some GUI form to optionally specify some limit on an + int value, but the user is allowed + to say 'I want the number not to be constrained by the maximum'. For another + example, consider a config parameter specifying how many threads the application + should launch. Leaving this parameter unspecified means that the application + should decide itself. For yet another example, consider a function returning + the index of the smallest element in a vector. + We need to be prepared for the situation, where the vector + is empty. Therefore a natural signature for such function would be: +

+
template <typename T>
+optional<size_t> find_smallest_elem(const std::vector<T>& vec);
+
+

+ Here, having received an empty vec + and having no size_t to return + is not a failure but a normal, + albeit irregular, situation. +

+

+ Another typical situation is to indicate that we do not have a value yet, + but we expect to have it later. This notion can be used in implementing solutions + like lazy initialization or a two-phase initialization. +

+

+ optional can be used to take + a non-DefaultConstructible type T and create a sibling type with a default + constructor. This is a way to add a null-state to any + type that doesn't have it already. +

+

+ Sometimes type T already + provides a built-in null-state, but it may still be useful to wrap it into + optional. Consider std::string. + When you read a piece of text from a GUI form or a DB table, it is hardly + ever that the empty string indicates anything else but a missing text. And + some data bases do not even distinguish between a null string entry and a + non-null string of length 0. Still, it may be practical to use optional<string> + to indicate in the returned type that we want to treat the empty string in + a special dedicated program path: +

+
if(boost::optional<std::string> name = ask_user_name()) {
+    assert(*name != "");
+    logon_as(*name);
+}
+else {
+    skip_logon();
+}
+
+

+ In the example above, the assertion indicates that if we choose to use this + technique, we must translate the empty string state to an optional object + with no contained value (inside function ask_user_name). +

+
+ + Not + recommended usages +
+

+ It is not recommended to use optional + to indicate that we were not able to compute a value because of a failure. + It is difficult to define what a failure is, but it usually has one common + characteristic: an associated information on the cause of the failure. This + can be the type and member data of an exception object, or an error code. + It is a bad design to signal a failure and not inform about the cause. If + you do not want to use exceptions, and do not like the fact that by returning + error codes you cannot return the computed value, you can use Expected + library. It is sort of Boost.Variant + that contains either a computed value or a reason why the computation failed. +

+

+ Sometimes the distinction into what is a failure and what is a valid but + irregular result is blurry and depends on a particular usage and personal + preference. Consider a function that converts a string + to an int. Is it a failure that + you cannot convert? It might in some cases, but in other you may call it + exactly for the purpose of figuring out if a given string + is convertible, and you are not even interested in the resulting value. Sometimes + when a conversion fails you may not consider it a failure, but you need to + know why it cannot be converted; for instance at which character it is determined + that the conversion is impossible. In this case returning optional<T> + will not suffice. Finally, there is a use case where an input string that + does not represent an int is + not a failure condition, but during the conversion we use resources whose + acquisition may fail. In that case the natural representation is to both + return optional<int> and + signal failure: +

+
optional<int> convert1(const string& str); // throws
+expected<ErrorT, optional<int>> convert2(const string& str); // return either optional or error
+
+
+ + + +
+
+
+PrevUpHomeNext +
+ + diff --git a/doc/html/index.html b/doc/html/index.html index 297e7be..51418c3 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -55,6 +55,10 @@
Motivation
Design Overview
+
When to + use Optional
+
Relational + operators
Optional references
Rebinding @@ -129,7 +133,7 @@

- +

Last revised: June 06, 2014 at 21:50:26 GMT

Last revised: June 08, 2014 at 14:17:56 GMT


diff --git a/doc/html/optional/tutorial.html b/doc/html/optional/tutorial.html index f09045f..7b84efd 100644 --- a/doc/html/optional/tutorial.html +++ b/doc/html/optional/tutorial.html @@ -29,6 +29,10 @@