C++11/14/17 std::optional with functional-style extensions and reference support
Single header implementation of
std::optionalwith functional-style extensions and support for references.
std::optionalis the preferred way to represent an object which may or may not have a value. Unfortunately, chaining together many computations which may or may not produce a value can be verbose, as empty-checking code will be mixed in with the actual programming logic. This implementation provides a number of utilities to make coding with
optionalcleaner.
For example, instead of writing this code:
std::optionalget_cute_cat (const image& img) { auto cropped = crop_to_cat(img); if (!cropped) { return std::nullopt; }
auto with_tie = add_bow_tie(*cropped); if (!with_tie) { return std::nullopt; } auto with_sparkles = make_eyes_sparkle(*with_tie); if (!with_sparkles) { return std::nullopt; } return add_rainbow(make_smaller(*with_sparkles));
}
You can do this:
tl::optionalget_cute_cat (const image& img) { return crop_to_cat(img) .and_then(add_bow_tie) .and_then(make_eyes_sparkle) .map(make_smaller) .map(add_rainbow); }
The interface is the same as
std::optional, but the following member functions are also defined. Explicit types are for clarity.
map: carries out some operation on the stored object if there is one.
tl::optional<:size_t> s = opt_string.map(&std::string::size);
and_then: like
map, but for operations which return a
tl::optional.
tl::optional stoi (const std::string& s);
tl::optional i = opt_string.and_then(stoi);
or_else: calls some function if there is no value stored.
opt.or_else([] { throw std::runtime_error{"oh no"}; });
map_or: carries out a
mapif there is a value, otherwise returns a default value.
tl::optional<:size_t> s = opt_string.map_or(&std::string::size, 0);
map_or_else: carries out a
mapif there is a value, otherwise returns the result of a given default function.
std::size_t get_default();
tl::optional<:size_t> s = opt_string.map_or_else(&std::string::size, get_default);
conjunction: returns the argument if a value is stored in the optional, otherwise an empty optional.
tl::make_optional(42).conjunction(13); //13
tl::optional{}.conjunction(13); //empty
disjunction: returns the argument if the optional is empty, otherwise the current value.
tl::make_optional(42).disjunction(13); //42
tl::optional{}.disjunction(13); //13
take: returns the current value, leaving the optional empty.
opt_string.take().map(&std::string::size); //opt_string now empty;
In addition to those member functions, optional references are also supported:
int i = 42; tl::optional o = i; *o == 42; //true i = 12; *o == 12; //true &*o == &i; //true
Assignment has rebind semantics rather than assign-through semantics:
int j = 8; o = j;&*o == &j; //true
Tested on:
This library also serves as an implementation of WG21 standards paper P0798R0: Monadic operations for std::optional. This paper proposes adding
map,
and_then, and
or_elseto
std::optional.
To the extent possible under law, Sy Brand has waived all copyright and related or neighboring rights to the
optionallibrary. This work is published from: United Kingdom.