diff --git a/README.md b/README.md index 99eedc4..c275b04 100644 --- a/README.md +++ b/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` in [expected.hpp](include/boost/variant2/expected.hpp) -that is an extended version of `expected` 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` is an almost conforming -implementation of `std::variant` with the following differences: - -* A converting constructor from, e.g. `variant` to - `variant` is provided as an extension; -* The reverse operation, going from `variant` to - `variant` is provided as the member function `subset`. - (This operation can throw if the current state of the variant cannot be - represented.) -* `variant` 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` 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`. - -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. diff --git a/doc/expected.md b/doc/expected.md deleted file mode 100644 index 8573f11..0000000 --- a/doc/expected.md +++ /dev/null @@ -1,305 +0,0 @@ -# expected - -## Description - -The class `expected` presented here is an extended version of `expected` 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`](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`. - -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 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 log( double x ); - - } // namespace lib2 - -In this proposal, combining the results of `lib1::div` and `lib2::log` can be achieved via -simple composition: - - expected 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 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`, -as in D0323R2, but there are two differences. First, `bad_expected_access` objects -derive from a common base `bad_expected_access` 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` has been called `unexpected_` and is -an alias for `variant`. It is unfortunately not possible to use the name `unexpected`, -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` can be converted to `expected` 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 { - - 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>( 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 using unexpected_ = variant; - - // bad_expected_access - - template class bad_expected_access; - - template<> class bad_expected_access: public std::exception - { - public: - - bad_expected_access() noexcept; - char const * what() const noexcept; - }; - - template class bad_expected_access: public bad_expected_access - { - public: - - explicit bad_expected_access( E const& e ); - E error() const; - }; - - // throw_on_unexpected - - template 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 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 - constexpr expected( unexpected_ const& x ); - - template - constexpr expected( unexpected_&& x ); - - // conversion constructor - - template - constexpr expected( expected const& x ); - - template - constexpr expected( expected&& x ); - - // emplace - - template void emplace( A&&... a ); - template void emplace( std::initializer_list 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 constexpr bool has_error() const noexcept; - constexpr bool has_error() const noexcept; - - // error access - - unexpected_ unexpected() const; - - template constexpr E2 error() const noexcept; - constexpr /*see below*/ error() const noexcept; - - // error mapping - - template /*see below*/ remap_errors( F&& f ) const; - expected remap_errors() const; - - // then - - template /*see below*/ operator>>( F&& f ) const; - }; - - template - inline constexpr bool operator==( expected const& x1, expected const& x2 ); - - template - inline constexpr bool operator!=( expected const& x1, expected const& x2 ); - - template - inline void swap( expected& x1, expected& x2 ) noexcept( /*see below*/ ); - - // is_expected - - template struct is_expected; - - } // namespace variant2 - } // namespace boost - -## Reference - -... diff --git a/doc/html/variant2.html b/doc/html/variant2.html index 05ee230..ab14090 100644 --- a/doc/html/variant2.html +++ b/doc/html/variant2.html @@ -1270,6 +1270,10 @@ 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
@@ -2296,6 +2300,11 @@ in T…​.

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).

+
@@ -2369,15 +2378,16 @@ Otherwise, throws bad_variant_access.

-
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).

+
@@ -2435,7 +2445,7 @@ the zero-based index of U in T…​.

Returns:
-

v.index() == w.index && get<I>(v) == get<I>(w), where I +

v.index() == w.index() && get<I>(v) == get<I>(w), where I is v.index().

@@ -2478,7 +2488,7 @@ is v.index().

Returns:
-

v.index() < w.index || (v.index() == w.index && get<I>(v) < get<I>(w)), +

v.index() < w.index() || (v.index() == w.index() && get<I>(v) < get<I>(w)), where I is v.index().

@@ -2521,7 +2531,7 @@ where I is v.index().

Returns:
-

v.index() < w.index || (v.index() == w.index && get<I>(v) <= get<I>(w)), +

v.index() < w.index() || (v.index() == w.index() && get<I>(v) <= get<I>(w)), where I is v.index().

@@ -2633,7 +2643,7 @@ the Boost Software License, Versi