forked from boostorg/variant2
Merge branch 'develop' into feature/documentation
This commit is contained in:
53
README.md
53
README.md
@ -1,64 +1,21 @@
|
||||
# variant2
|
||||
|
||||
This repository contains a never-valueless C++11/14/17 implementation of
|
||||
[std::variant](http://en.cppreference.com/w/cpp/utility/variant) in
|
||||
[variant.hpp](include/boost/variant2/variant.hpp) and an implementation of
|
||||
`expected<T, E...>` in [expected.hpp](include/boost/variant2/expected.hpp)
|
||||
that is an extended version of `expected<T, E>` as proposed in
|
||||
[P0323R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0323r1.pdf)
|
||||
and the subsequent
|
||||
[D0323R2](https://github.com/viboes/std-make/blob/master/doc/proposal/expected/d0323r2.md).
|
||||
This repository contains a never-valueless, strong guarantee, C++11/14/17
|
||||
implementation of [std::variant](http://en.cppreference.com/w/cpp/utility/variant).
|
||||
See [the documentation](https://pdimov.github.io/variant2/) for more information.
|
||||
|
||||
The code requires [Boost.Mp11](https://github.com/boostorg/mp11) and
|
||||
Boost.Config.
|
||||
|
||||
The repository is intended to be placed into the `libs/variant2` directory of
|
||||
a Boost clone or release, but the header `variant.hpp` will also work
|
||||
[standalone](https://godbolt.org/z/CTZztA).
|
||||
[standalone](https://godbolt.org/z/nVUNKX).
|
||||
|
||||
Supported compilers:
|
||||
|
||||
* g++ 4.8 or later with `-std=c++11` or above
|
||||
* clang++ 3.5 or later with `-std=c++11` or above
|
||||
* Visual Studio 2015, 2017
|
||||
* Visual Studio 2015, 2017, 2019
|
||||
|
||||
Tested on [Travis](https://travis-ci.org/pdimov/variant2/) and
|
||||
[Appveyor](https://ci.appveyor.com/project/pdimov/variant2/).
|
||||
|
||||
## variant.hpp
|
||||
|
||||
The class `boost::variant2::variant<T...>` is an almost conforming
|
||||
implementation of `std::variant` with the following differences:
|
||||
|
||||
* A converting constructor from, e.g. `variant<int, float>` to
|
||||
`variant<float, double, int>` is provided as an extension;
|
||||
* The reverse operation, going from `variant<float, double, int>` to
|
||||
`variant<int, float>` is provided as the member function `subset<U...>`.
|
||||
(This operation can throw if the current state of the variant cannot be
|
||||
represented.)
|
||||
* `variant<T...>` is not trivial when all contained types are trivial.
|
||||
|
||||
To avoid going into a valueless-by-exception state, this implementation falls
|
||||
back to using double storage unless
|
||||
|
||||
* one of the alternatives is the type `monostate`,
|
||||
* one of the alternatives has a nonthrowing default constructor, or
|
||||
* all the contained types are nothrow move constructible.
|
||||
|
||||
If the first two bullets don't hold, but the third does, the variant uses
|
||||
single storage, but `emplace` constructs a temporary and moves it into place
|
||||
if the construction of the object can throw. In case this is undesirable, one
|
||||
can force `emplace` into always constructing in-place by adding `monostate` as
|
||||
one of the alternatives.
|
||||
|
||||
## expected.hpp
|
||||
|
||||
The class `boost::variant2::expected<T, E...>` represents the return type of
|
||||
an operation that may potentially fail. It contains either the expected result
|
||||
of type `T`, or a reason for the failure, of one of the error types in `E...`.
|
||||
Internally, this is stored as `variant<T, E...>`.
|
||||
|
||||
See [its documentation](doc/expected.md) for more information.
|
||||
|
||||
Note that, while `variant` is production quality, `expected` is still a work
|
||||
in progress and has no test suite yet.
|
||||
|
305
doc/expected.md
305
doc/expected.md
@ -1,305 +0,0 @@
|
||||
# expected<T, E...>
|
||||
|
||||
## Description
|
||||
|
||||
The class `expected<T, E...>` presented here is an extended version of `expected<T, E>` as
|
||||
proposed in [P0323R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0323r1.pdf)
|
||||
and the subsequent [D0323R2](https://github.com/viboes/std-make/blob/master/doc/proposal/expected/d0323r2.md).
|
||||
|
||||
The main difference is that this class takes more than one error type, which makes it more
|
||||
flexible. One example of a type of the `expected` family, [`outcome<T>`](https://ned14.github.io/boost.outcome/),
|
||||
on failure can store either an error of type `std::error_code`, or an exception in the form of `std::exception_ptr`.
|
||||
This can be represented naturally in this implementation via `expected<T, std::error_code, std::exception_ptr>`.
|
||||
|
||||
In addition, libraries would generally differ in their choice of error types. It would be a
|
||||
common need in practice of having to combine the results of calling two different libraries,
|
||||
each with its own error type. Library 1 may use `lib1::error`:
|
||||
|
||||
namespace lib1
|
||||
{
|
||||
|
||||
enum class error
|
||||
{
|
||||
division_by_zero,
|
||||
other_error
|
||||
};
|
||||
|
||||
expected<double, error> div( double x, double y );
|
||||
|
||||
} // namespace lib1
|
||||
|
||||
while Library 2 might define its own `lib2::error`:
|
||||
|
||||
namespace lib2
|
||||
{
|
||||
|
||||
enum class error
|
||||
{
|
||||
division_by_zero,
|
||||
negative_logarithm
|
||||
};
|
||||
|
||||
expected<double, error> log( double x );
|
||||
|
||||
} // namespace lib2
|
||||
|
||||
In this proposal, combining the results of `lib1::div` and `lib2::log` can be achieved via
|
||||
simple composition:
|
||||
|
||||
expected<double, lib1::error, lib2::error> log_div_mul( double x, double y, double m )
|
||||
{
|
||||
auto r1 = lib1::div( x, y );
|
||||
if( !r1 ) return r1.unexpected();
|
||||
|
||||
auto r2 = lib2::log( r1.value() );
|
||||
if( !r2 ) return r2.unexpected();
|
||||
|
||||
return m * r2.value();
|
||||
}
|
||||
|
||||
An alternative approach that requires more effort is also supported:
|
||||
|
||||
enum class common_error
|
||||
{
|
||||
division_by_zero,
|
||||
negative_logarithm,
|
||||
other_error,
|
||||
unknown_error
|
||||
};
|
||||
|
||||
common_error make_common_error( lib1::error e );
|
||||
common_error make_common_error( lib2::error e );
|
||||
|
||||
expected<double, common_error> log_div_mul2( double x, double y, double m )
|
||||
{
|
||||
static const auto rm = []( auto x ) { return make_common_error(x); };
|
||||
|
||||
auto r1 = lib1::div( x, y ).remap_errors( rm );
|
||||
if( !r1 ) return r1.unexpected();
|
||||
|
||||
auto r2 = lib2::log( r1.value() ).remap_errors( rm );
|
||||
if( !r2 ) return r2.unexpected();
|
||||
|
||||
return m * r2.value();
|
||||
}
|
||||
|
||||
`std::error_code` is a very good choice for a common error type, and it's supported
|
||||
natively by the overload of `.remap_errors()` that takes no arguments, which uses
|
||||
calls to `make_error_code` to translate the errors.
|
||||
|
||||
When an attempt to access the value via `r.value()` is made and an error is present,
|
||||
an exception is thrown. By default, this exception is of type `bad_expected_access<E>`,
|
||||
as in D0323R2, but there are two differences. First, `bad_expected_access<E>` objects
|
||||
derive from a common base `bad_expected_access<void>` so that they can be caught at
|
||||
points where the set of possible `E` is unknown.
|
||||
|
||||
Second, the thrown exception can be customized. The implementation calls
|
||||
`throw_on_unexpected(e)` unqualified, where `e` is the error object, and the user can
|
||||
define such a function in the namespace of the type of `e`. Two specialized overloads
|
||||
of `throw_on_unexpected` are provided, one for `std::error_code`, which throws the
|
||||
corresponding `std::system_error`, and one for `std::exception_ptr`, which rethrows
|
||||
the exception stored in it.
|
||||
|
||||
For example, `lib1` from above may customize the exceptions associated with `lib1::error`
|
||||
via the following:
|
||||
|
||||
namespace lib1
|
||||
{
|
||||
|
||||
enum class error
|
||||
{
|
||||
division_by_zero,
|
||||
other_error
|
||||
};
|
||||
|
||||
class exception: public std::exception
|
||||
{
|
||||
private:
|
||||
|
||||
error e_;
|
||||
|
||||
public:
|
||||
|
||||
explicit exception( error e ): e_( e ) {}
|
||||
virtual const char * what() const noexcept;
|
||||
};
|
||||
|
||||
void throw_on_unexpected( error e )
|
||||
{
|
||||
throw exception( e );
|
||||
}
|
||||
|
||||
} // namespace lib1
|
||||
|
||||
In this implementation, `unexpected_type<E...>` has been called `unexpected_<T...>` and is
|
||||
an alias for `variant<T...>`. It is unfortunately not possible to use the name `unexpected<T...>`,
|
||||
because a function `std::unexpected` already exists.
|
||||
|
||||
The `make_...` helper functions have been omitted as unnecessary; class template argument deduction
|
||||
as in `expected{ 1.0 }` or `unexpected_{ lib1::division_by_zero }` suffices.
|
||||
|
||||
Other functions have also been dropped as unnecessary, not providing sufficient value, dangerous, or
|
||||
a combination of the three, although the decision of what to include isn't final at this point. The aim
|
||||
is to produce a minimal interface that still covers the use cases.
|
||||
|
||||
`expected<T, E1...>` can be converted to `expected<T, E2...>` if all error types in `E1...` are
|
||||
also in `E2...`. This allows composition as in the example above. Whether value convertibility ought
|
||||
to also be supported is an open question.
|
||||
|
||||
A single monadic operation ("bind") is supported in the form of `operator>>`, allowing
|
||||
|
||||
auto log_div_mul3( double x, double y, double m )
|
||||
{
|
||||
return lib1::div( x, y ) >> [&]( auto && r1 ) {
|
||||
|
||||
return lib2::log( r1 ) >> [&]( auto && r2 ) -> expected<double, lib1::error, lib2::error> {
|
||||
|
||||
return m * r2;
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
as well as the more concise in this example, although limited in utility for real world scenarios,
|
||||
|
||||
auto log_div_mul3( double x, double y, double m )
|
||||
{
|
||||
return lib1::div( x, y ) >> std::bind<expected<double, lib1::error, lib2::error>>( lib2::log, _1 ) >> m * _1;
|
||||
}
|
||||
|
||||
The more traditional name `then` was also a candidate for this operation, but `operator>>` has two advantages;
|
||||
it avoids the inevitable naming debates and does not require parentheses around the continuation lambda.
|
||||
|
||||
## Synopsis
|
||||
|
||||
// unexpected_
|
||||
|
||||
template<class... E> using unexpected_ = variant<E...>;
|
||||
|
||||
// bad_expected_access
|
||||
|
||||
template<class E = void> class bad_expected_access;
|
||||
|
||||
template<> class bad_expected_access<void>: public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
bad_expected_access() noexcept;
|
||||
char const * what() const noexcept;
|
||||
};
|
||||
|
||||
template<class E> class bad_expected_access: public bad_expected_access<void>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit bad_expected_access( E const& e );
|
||||
E error() const;
|
||||
};
|
||||
|
||||
// throw_on_unexpected
|
||||
|
||||
template<class E> void throw_on_unexpected( E const& e );
|
||||
void throw_on_unexpected( std::error_code const& e );
|
||||
void throw_on_unexpected( std::exception_ptr const& e );
|
||||
|
||||
// expected
|
||||
|
||||
template<class T, class... E> class expected
|
||||
{
|
||||
public:
|
||||
|
||||
// value constructors
|
||||
|
||||
constexpr expected() noexcept( /*see below*/ );
|
||||
|
||||
constexpr expected( T const& t ) noexcept( /*see below*/ );
|
||||
constexpr expected( T&& t ) noexcept( /*see below*/ );
|
||||
|
||||
// unexpected constructor
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( unexpected_<E2...> const& x );
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( unexpected_<E2...>&& x );
|
||||
|
||||
// conversion constructor
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( expected<T, E2...> const& x );
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( expected<T, E2...>&& x );
|
||||
|
||||
// emplace
|
||||
|
||||
template<class... A> void emplace( A&&... a );
|
||||
template<class V, class... A> void emplace( std::initializer_list<V> il, A&&... a );
|
||||
|
||||
// swap
|
||||
|
||||
void swap( expected& r ) noexcept( /*see below*/ );
|
||||
|
||||
// value queries
|
||||
|
||||
constexpr bool has_value() const noexcept;
|
||||
constexpr explicit operator bool() const noexcept;
|
||||
|
||||
// checked value access
|
||||
|
||||
constexpr T& value() &;
|
||||
constexpr T const& value() const&;
|
||||
constexpr T&& value() &&;
|
||||
constexpr T const&& value() const&&;
|
||||
|
||||
// unchecked value access
|
||||
|
||||
T* operator->() noexcept;
|
||||
T const* operator->() const noexcept;
|
||||
|
||||
T& operator*() & noexcept;
|
||||
T const& operator*() const & noexcept;
|
||||
T&& operator*() && noexcept;
|
||||
T const&& operator*() const && noexcept;
|
||||
|
||||
// error queries
|
||||
|
||||
template<class E2> constexpr bool has_error() const noexcept;
|
||||
constexpr bool has_error() const noexcept;
|
||||
|
||||
// error access
|
||||
|
||||
unexpected_<E...> unexpected() const;
|
||||
|
||||
template<class E2> constexpr E2 error() const noexcept;
|
||||
constexpr /*see below*/ error() const noexcept;
|
||||
|
||||
// error mapping
|
||||
|
||||
template<class F> /*see below*/ remap_errors( F&& f ) const;
|
||||
expected<T, std::error_code> remap_errors() const;
|
||||
|
||||
// then
|
||||
|
||||
template<class F> /*see below*/ operator>>( F&& f ) const;
|
||||
};
|
||||
|
||||
template<class T, class... E>
|
||||
inline constexpr bool operator==( expected<T, E...> const& x1, expected<T, E...> const& x2 );
|
||||
|
||||
template<class T, class... E>
|
||||
inline constexpr bool operator!=( expected<T, E...> const& x1, expected<T, E...> const& x2 );
|
||||
|
||||
template<class T, class... E>
|
||||
inline void swap( expected<T, E...>& x1, expected<T, E...>& x2 ) noexcept( /*see below*/ );
|
||||
|
||||
// is_expected
|
||||
|
||||
template<class T> struct is_expected;
|
||||
|
||||
} // namespace variant2
|
||||
} // namespace boost
|
||||
|
||||
## Reference
|
||||
|
||||
...
|
@ -1270,6 +1270,10 @@ public:
|
||||
} // namespace boost</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In the descriptions that follow, let <code>i</code> be in the range <code>[0, sizeof…​(T))</code>,
|
||||
and <code>Ti</code> be the <code>i</code>-th type in <code>T…​</code>.</p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="ref_constructors">Constructors</h5>
|
||||
<div class="listingblock">
|
||||
@ -2296,6 +2300,11 @@ in <code>T…​</code>.</p>
|
||||
<p>If <code>v.index()</code> is <code>I</code>, returns a reference to the object stored in
|
||||
the variant. Otherwise, throws <code>bad_variant_access</code>.</p>
|
||||
</dd>
|
||||
<dt class="hdlist1">Remarks: </dt>
|
||||
<dd>
|
||||
<p>These functions do not participate in overload resolution
|
||||
unless <code>I</code> < <code>sizeof…​(T)</code>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</li>
|
||||
@ -2369,15 +2378,16 @@ Otherwise, throws <code>bad_variant_access</code>.</p>
|
||||
<p></p>
|
||||
<div class="dlist">
|
||||
<dl>
|
||||
<dt class="hdlist1">Requires: </dt>
|
||||
<dd>
|
||||
<p><code>I < sizeof…​(U)</code>. Otherwise, the program is ill-formed.</p>
|
||||
</dd>
|
||||
<dt class="hdlist1">Effects: </dt>
|
||||
<dd>
|
||||
<p>A pointer to the value stored in the variant, if
|
||||
<code>v != nullptr && v->index() == I</code>. Otherwise, <code>nullptr</code>.</p>
|
||||
</dd>
|
||||
<dt class="hdlist1">Remarks: </dt>
|
||||
<dd>
|
||||
<p>These functions do not participate in overload resolution
|
||||
unless <code>I</code> < <code>sizeof…​(T)</code>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</li>
|
||||
@ -2435,7 +2445,7 @@ the zero-based index of <code>U</code> in <code>T…​</code>.</p>
|
||||
<dl>
|
||||
<dt class="hdlist1">Returns: </dt>
|
||||
<dd>
|
||||
<p><code>v.index() == w.index && get<I>(v) == get<I>(w)</code>, where <code>I</code>
|
||||
<p><code>v.index() == w.index() && get<I>(v) == get<I>(w)</code>, where <code>I</code>
|
||||
is <code>v.index()</code>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
@ -2478,7 +2488,7 @@ is <code>v.index()</code>.</p>
|
||||
<dl>
|
||||
<dt class="hdlist1">Returns: </dt>
|
||||
<dd>
|
||||
<p><code>v.index() < w.index || (v.index() == w.index && get<I>(v) < get<I>(w))</code>,
|
||||
<p><code>v.index() < w.index() || (v.index() == w.index() && get<I>(v) < get<I>(w))</code>,
|
||||
where <code>I</code> is <code>v.index()</code>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
@ -2521,7 +2531,7 @@ where <code>I</code> is <code>v.index()</code>.</p>
|
||||
<dl>
|
||||
<dt class="hdlist1">Returns: </dt>
|
||||
<dd>
|
||||
<p><code>v.index() < w.index || (v.index() == w.index && get<I>(v) <= get<I>(w))</code>,
|
||||
<p><code>v.index() < w.index() || (v.index() == w.index() && get<I>(v) <= get<I>(w))</code>,
|
||||
where <code>I</code> is <code>v.index()</code>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
@ -2633,7 +2643,7 @@ the <a href="http://www.boost.org/LICENSE_1_0.txt">Boost Software License, Versi
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2019-05-12 02:21:08 +0300
|
||||
Last updated 2019-05-12 18:44:13 +0300
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
|
@ -247,6 +247,9 @@ public:
|
||||
} // namespace boost
|
||||
```
|
||||
|
||||
In the descriptions that follow, let `i` be in the range `[0, sizeof...(T))`,
|
||||
and `Ti` be the `i`-th type in `T...`.
|
||||
|
||||
#### Constructors
|
||||
|
||||
```
|
||||
@ -707,6 +710,8 @@ template<size_t I, class... T>
|
||||
+
|
||||
Effects: :: If `v.index()` is `I`, returns a reference to the object stored in
|
||||
the variant. Otherwise, throws `bad_variant_access`.
|
||||
Remarks: :: These functions do not participate in overload resolution
|
||||
unless `I` < `sizeof...(T)`.
|
||||
|
||||
```
|
||||
template<class U, class... T>
|
||||
@ -747,9 +752,10 @@ template<size_t I, class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Requires: :: `I < sizeof...(U)`. Otherwise, the program is ill-formed.
|
||||
Effects: :: A pointer to the value stored in the variant, if
|
||||
`v != nullptr && v\->index() == I`. Otherwise, `nullptr`.
|
||||
Remarks: :: These functions do not participate in overload resolution
|
||||
unless `I` < `sizeof...(T)`.
|
||||
|
||||
```
|
||||
template<class U, class... T>
|
||||
@ -778,7 +784,7 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `v.index() == w.index && get<I>(v) == get<I>(w)`, where `I`
|
||||
Returns: :: `v.index() == w.index() && get<I>(v) == get<I>(w)`, where `I`
|
||||
is `v.index()`.
|
||||
|
||||
```
|
||||
@ -797,7 +803,7 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `v.index() < w.index || (v.index() == w.index && get<I>(v) < get<I>(w))`,
|
||||
Returns: :: `v.index() < w.index() || (v.index() == w.index() && get<I>(v) < get<I>(w))`,
|
||||
where `I` is `v.index()`.
|
||||
|
||||
```
|
||||
@ -816,7 +822,7 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `v.index() < w.index || (v.index() == w.index && get<I>(v) \<= get<I>(w))`,
|
||||
Returns: :: `v.index() < w.index() || (v.index() == w.index() && get<I>(v) \<= get<I>(w))`,
|
||||
where `I` is `v.index()`.
|
||||
|
||||
```
|
||||
|
@ -1,446 +0,0 @@
|
||||
#ifndef BOOST_VARIANT2_EXPECTED_HPP_INCLUDED
|
||||
#define BOOST_VARIANT2_EXPECTED_HPP_INCLUDED
|
||||
|
||||
// Copyright 2017 Peter Dimov.
|
||||
//
|
||||
// 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
|
||||
|
||||
#ifndef BOOST_VARIANT2_VARIANT_HPP_INCLUDED
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#endif
|
||||
#include <boost/core/demangle.hpp>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <cassert>
|
||||
|
||||
//
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace variant2
|
||||
{
|
||||
|
||||
// unexpected_
|
||||
|
||||
template<class... E> using unexpected_ = variant<E...>;
|
||||
|
||||
// bad_expected_access
|
||||
|
||||
template<class E = void> class bad_expected_access;
|
||||
|
||||
template<> class bad_expected_access<void>: public std::exception
|
||||
{
|
||||
private:
|
||||
|
||||
std::string msg_;
|
||||
|
||||
public:
|
||||
|
||||
bad_expected_access() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
explicit bad_expected_access( std::string&& msg ) noexcept: msg_( std::move(msg) ) // extension
|
||||
{
|
||||
}
|
||||
|
||||
char const * what() const noexcept
|
||||
{
|
||||
return msg_.empty()? "bad_expected_access<>": msg_.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class E, class En = std::enable_if_t<!std::is_enum<E>::value>> std::string add_value( E const& /*e*/ )
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
template<class E, class E1 = void, class E2 = std::enable_if_t<std::is_enum<E>::value>> std::string add_value( E const& e )
|
||||
{
|
||||
return ": " + std::to_string( static_cast<int>(e) );
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class E> class bad_expected_access: public bad_expected_access<void>
|
||||
{
|
||||
private:
|
||||
|
||||
E e_;
|
||||
|
||||
public:
|
||||
|
||||
explicit bad_expected_access( E const& e )
|
||||
: bad_expected_access<void>( "bad_expected_access<" + boost::core::demangle( typeid(E).name() ) + ">" + variant2::detail::add_value( e ) ), e_( e )
|
||||
{
|
||||
}
|
||||
|
||||
E error() const
|
||||
{
|
||||
return e_;
|
||||
}
|
||||
};
|
||||
|
||||
// throw_on_unexpected
|
||||
|
||||
template<class E> void throw_on_unexpected( E const& /*e*/ )
|
||||
{
|
||||
}
|
||||
|
||||
void throw_on_unexpected( std::error_code const & e )
|
||||
{
|
||||
throw std::system_error( e );
|
||||
}
|
||||
|
||||
void throw_on_unexpected( std::exception_ptr const & e )
|
||||
{
|
||||
if( e )
|
||||
{
|
||||
std::rethrow_exception( e );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_expected_access<>( "bad_expected_access<>: null exception_ptr" );
|
||||
}
|
||||
}
|
||||
|
||||
// expected
|
||||
|
||||
template<class T, class... E> class expected;
|
||||
|
||||
template<class T> struct is_expected: std::false_type {};
|
||||
template<class T, class... E> struct is_expected<expected<T, E...>>: std::true_type {};
|
||||
|
||||
template<class T, class... E> class expected
|
||||
{
|
||||
private:
|
||||
|
||||
variant<T, E...> v_;
|
||||
|
||||
private:
|
||||
|
||||
void _bad_access() const
|
||||
{
|
||||
mp_with_index<mp_size<expected>>( v_.index(), [&]( auto I )
|
||||
{
|
||||
if( I == 0 )
|
||||
{
|
||||
throw bad_expected_access<>( "bad_expected_access<>: value present on error request" );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const & e = get<I>(v_);
|
||||
|
||||
throw_on_unexpected( e );
|
||||
throw bad_expected_access<std::decay_t<decltype(e)>>( e );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// value constructors
|
||||
|
||||
constexpr expected() noexcept( std::is_nothrow_default_constructible<T>::value )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr expected( T const& t ) noexcept( std::is_nothrow_copy_constructible<T>::value ): v_( in_place_index<0>, t )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr expected( T && t ) noexcept( std::is_nothrow_move_constructible<T>::value ): v_( in_place_index<0>, std::move(t) )
|
||||
{
|
||||
}
|
||||
|
||||
// template<class U> constexpr expected( U && u ); where U in E...?
|
||||
|
||||
// in-place constructor?
|
||||
|
||||
// unexpected constructor
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_copy_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( unexpected_<E2...> const & x ): v_( x )
|
||||
{
|
||||
}
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_move_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( unexpected_<E2...> && x ): v_( std::move(x) )
|
||||
{
|
||||
}
|
||||
|
||||
// conversion constructor
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_copy_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( expected<T, E2...> const & x ): v_( x.v_ )
|
||||
{
|
||||
}
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_move_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( expected<T, E2...> && x ): v_( std::move(x.v_) )
|
||||
{
|
||||
}
|
||||
|
||||
// emplace
|
||||
|
||||
template<class... A> void emplace( A&&... a )
|
||||
{
|
||||
v_.emplace( std::forward<A>(a)... );
|
||||
}
|
||||
|
||||
template<class V, class... A> void emplace( std::initializer_list<V> il, A&&... a )
|
||||
{
|
||||
v_.emplace( il, std::forward<A>(a)... );
|
||||
}
|
||||
|
||||
// swap
|
||||
|
||||
void swap( expected & r ) noexcept( noexcept( v_.swap( r.v_ ) ) )
|
||||
{
|
||||
v_.swap( r.v_ );
|
||||
}
|
||||
|
||||
// value queries
|
||||
|
||||
constexpr bool has_value() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
// checked value access
|
||||
|
||||
constexpr T& value() &
|
||||
{
|
||||
if( !has_value() )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
|
||||
constexpr T const& value() const&
|
||||
{
|
||||
if( !has_value() )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
|
||||
constexpr T&& value() &&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
constexpr T const&& value() const&&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
// unchecked value access
|
||||
|
||||
T* operator->() noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T const* operator->() const noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T& operator*() & noexcept
|
||||
{
|
||||
T* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T const& operator*() const & noexcept
|
||||
{
|
||||
T const* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T&& operator*() && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
T const&& operator*() const && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
// error queries
|
||||
|
||||
template<class E2> constexpr bool has_error() const noexcept
|
||||
{
|
||||
using I = mp_find<expected, E2>;
|
||||
return v_.index() == I::value;
|
||||
}
|
||||
|
||||
constexpr bool has_error() const noexcept
|
||||
{
|
||||
static_assert( sizeof...(E) == 1, "has_error() is only valid when there is a single E" );
|
||||
return has_error<mp_first<expected>>();
|
||||
}
|
||||
|
||||
// error access
|
||||
|
||||
unexpected_<E...> unexpected() const
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return v_.template subset<E...>();
|
||||
}
|
||||
|
||||
template<class E2> constexpr E2 error() const noexcept
|
||||
{
|
||||
using I = mp_find<expected, E2>;
|
||||
|
||||
if( v_.index() != I::value )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return get<I>( v_ );
|
||||
}
|
||||
|
||||
constexpr mp_first<expected> error() const noexcept
|
||||
{
|
||||
static_assert( sizeof...(E) == 1, "error() is only valid when there is a single E" );
|
||||
return error<mp_first<expected>>();
|
||||
}
|
||||
|
||||
// error mapping
|
||||
|
||||
private:
|
||||
|
||||
template<class F> struct Qret
|
||||
{
|
||||
template<class... A> using fn = decltype( std::declval<F>()( std::declval<A>()... ) );
|
||||
};
|
||||
|
||||
template<class F> using remapped = mp_append<expected<T>, mp_unique<mp_transform_q<Qret<F>, mp_list<E...>>>>;
|
||||
|
||||
template<class R, std::size_t I, class F, class V> static R _remap_error( mp_size_t<I>, F && f, V && v )
|
||||
{
|
||||
// return R( std::forward<F>(f)( std::forward<V>(v) ) );
|
||||
|
||||
auto e = std::forward<F>(f)( std::forward<V>(v) );
|
||||
|
||||
return unexpected_<decltype(e)>{ e };
|
||||
}
|
||||
|
||||
template<class R, class F, class V> static R _remap_error( mp_size_t<0>, F && /*f*/, V && v )
|
||||
{
|
||||
return R( std::forward<V>(v) );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
template<class F> remapped<F> remap_errors( F && f ) const
|
||||
{
|
||||
using R = remapped<F>;
|
||||
|
||||
return mp_with_index<mp_size<expected>>( v_.index(), [&]( auto I ) {
|
||||
|
||||
return this->_remap_error<R>( I, f, get<I>(v_) );
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
expected<T, std::error_code> remap_errors() const
|
||||
{
|
||||
using R = expected<T, std::error_code>;
|
||||
|
||||
auto f = []( auto const& e ){ return make_error_code(e); };
|
||||
|
||||
return mp_with_index<mp_size<expected>>( v_.index(), [&]( auto I ) {
|
||||
|
||||
return this->_remap_error<R>( I, f, get<I>(v_) );
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// then
|
||||
|
||||
private:
|
||||
|
||||
template<class F, class U> using then_result_ = decltype( std::declval<F>()( std::declval<U>() ) );
|
||||
|
||||
template<class F, class U, class R = then_result_<F, U>> using then_result = mp_if<is_expected<R>, R, expected<R, E...>>;
|
||||
|
||||
public:
|
||||
|
||||
template<class F> then_result<F, T const&> then( F && f ) const
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return std::forward<F>(f)( **this );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unexpected();
|
||||
}
|
||||
}
|
||||
|
||||
template<class F> then_result<F, T const&> operator>>( F && f ) const
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return std::forward<F>(f)( **this );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unexpected();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, class... E> inline constexpr bool operator==( expected<T, E...> const & x1, expected<T, E...> const & x2 )
|
||||
{
|
||||
return x1.v_ == x2.v_;
|
||||
}
|
||||
|
||||
template<class T, class... E> inline constexpr bool operator!=( expected<T, E...> const & x1, expected<T, E...> const & x2 )
|
||||
{
|
||||
return x1.v_ != x2.v_;
|
||||
}
|
||||
|
||||
template<class T, class... E> inline void swap( expected<T, E...> & x1, expected<T, E...> & x2 ) noexcept( noexcept( x1.swap( x2 ) ) )
|
||||
{
|
||||
x1.swap( x2 );
|
||||
}
|
||||
|
||||
} // namespace variant2
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_VARIANT2_EXPECTED_HPP_INCLUDED
|
@ -1310,11 +1310,6 @@ public:
|
||||
|
||||
// private accessors
|
||||
|
||||
constexpr int _real_index() const noexcept
|
||||
{
|
||||
return this->ix_;
|
||||
}
|
||||
|
||||
using variant_base::_get_impl;
|
||||
|
||||
// converting constructors (extension)
|
||||
|
Reference in New Issue
Block a user