From 3041fff85c4586f39d7e313dd8e37ec26dd2ba86 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sun, 12 May 2019 18:56:54 +0300 Subject: [PATCH 1/4] Remove expected --- doc/expected.md | 305 ------------------- include/boost/variant2/expected.hpp | 446 ---------------------------- 2 files changed, 751 deletions(-) delete mode 100644 doc/expected.md delete mode 100644 include/boost/variant2/expected.hpp 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/include/boost/variant2/expected.hpp b/include/boost/variant2/expected.hpp deleted file mode 100644 index 24a46fd..0000000 --- a/include/boost/variant2/expected.hpp +++ /dev/null @@ -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 -#endif -#include -#include -#include -#include -#include - -// - -namespace boost -{ -namespace variant2 -{ - -// unexpected_ - -template using unexpected_ = variant; - -// bad_expected_access - -template class bad_expected_access; - -template<> class bad_expected_access: 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::value>> std::string add_value( E const& /*e*/ ) -{ - return std::string(); -} - -template::value>> std::string add_value( E const& e ) -{ - return ": " + std::to_string( static_cast(e) ); -} - -} // namespace detail - -template class bad_expected_access: public bad_expected_access -{ -private: - - E e_; - -public: - - explicit bad_expected_access( E const& e ) - : bad_expected_access( "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 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 expected; - -template struct is_expected: std::false_type {}; -template struct is_expected>: std::true_type {}; - -template class expected -{ -private: - - variant v_; - -private: - - void _bad_access() const - { - mp_with_index>( v_.index(), [&]( auto I ) - { - if( I == 0 ) - { - throw bad_expected_access<>( "bad_expected_access<>: value present on error request" ); - } - else - { - auto const & e = get(v_); - - throw_on_unexpected( e ); - throw bad_expected_access>( e ); - } - }); - } - -public: - - // value constructors - - constexpr expected() noexcept( std::is_nothrow_default_constructible::value ) - { - } - - constexpr expected( T const& t ) noexcept( std::is_nothrow_copy_constructible::value ): v_( in_place_index<0>, t ) - { - } - - constexpr expected( T && t ) noexcept( std::is_nothrow_move_constructible::value ): v_( in_place_index<0>, std::move(t) ) - { - } - - // template constexpr expected( U && u ); where U in E...? - - // in-place constructor? - - // unexpected constructor - - template..., mp_contains, E2>...>, void>> - constexpr expected( unexpected_ const & x ): v_( x ) - { - } - - template..., mp_contains, E2>...>, void>> - constexpr expected( unexpected_ && x ): v_( std::move(x) ) - { - } - - // conversion constructor - - template..., mp_contains, E2>...>, void>> - constexpr expected( expected const & x ): v_( x.v_ ) - { - } - - template..., mp_contains, E2>...>, void>> - constexpr expected( expected && x ): v_( std::move(x.v_) ) - { - } - - // emplace - - template void emplace( A&&... a ) - { - v_.emplace( std::forward(a)... ); - } - - template void emplace( std::initializer_list il, A&&... a ) - { - v_.emplace( il, std::forward(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 constexpr bool has_error() const noexcept - { - using I = mp_find; - 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>(); - } - - // error access - - unexpected_ unexpected() const - { - if( has_value() ) - { - _bad_access(); - } - - return v_.template subset(); - } - - template constexpr E2 error() const noexcept - { - using I = mp_find; - - if( v_.index() != I::value ) - { - _bad_access(); - } - - return get( v_ ); - } - - constexpr mp_first error() const noexcept - { - static_assert( sizeof...(E) == 1, "error() is only valid when there is a single E" ); - return error>(); - } - - // error mapping - -private: - - template struct Qret - { - template using fn = decltype( std::declval()( std::declval()... ) ); - }; - - template using remapped = mp_append, mp_unique, mp_list>>>; - - template static R _remap_error( mp_size_t, F && f, V && v ) - { - // return R( std::forward(f)( std::forward(v) ) ); - - auto e = std::forward(f)( std::forward(v) ); - - return unexpected_{ e }; - } - - template static R _remap_error( mp_size_t<0>, F && /*f*/, V && v ) - { - return R( std::forward(v) ); - } - -public: - - template remapped remap_errors( F && f ) const - { - using R = remapped; - - return mp_with_index>( v_.index(), [&]( auto I ) { - - return this->_remap_error( I, f, get(v_) ); - - }); - } - - expected remap_errors() const - { - using R = expected; - - auto f = []( auto const& e ){ return make_error_code(e); }; - - return mp_with_index>( v_.index(), [&]( auto I ) { - - return this->_remap_error( I, f, get(v_) ); - - }); - } - - // then - -private: - - template using then_result_ = decltype( std::declval()( std::declval() ) ); - - template> using then_result = mp_if, R, expected>; - -public: - - template then_result then( F && f ) const - { - if( has_value() ) - { - return std::forward(f)( **this ); - } - else - { - return unexpected(); - } - } - - template then_result operator>>( F && f ) const - { - if( has_value() ) - { - return std::forward(f)( **this ); - } - else - { - return unexpected(); - } - } -}; - -template inline constexpr bool operator==( expected const & x1, expected const & x2 ) -{ - return x1.v_ == x2.v_; -} - -template inline constexpr bool operator!=( expected const & x1, expected const & x2 ) -{ - return x1.v_ != x2.v_; -} - -template inline void swap( expected & x1, expected & x2 ) noexcept( noexcept( x1.swap( x2 ) ) ) -{ - x1.swap( x2 ); -} - -} // namespace variant2 -} // namespace boost - -#endif // #ifndef BOOST_VARIANT2_EXPECTED_HPP_INCLUDED From e686dab3eaf8428ed78d0c304f1b50ad657b65a4 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sun, 12 May 2019 19:02:59 +0300 Subject: [PATCH 2/4] Update README --- README.md | 53 +++++------------------------------------------------ 1 file changed, 5 insertions(+), 48 deletions(-) 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. From a083667a9640e97729a47f80f7607351ec89f367 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sun, 12 May 2019 19:19:55 +0300 Subject: [PATCH 3/4] Apply corrections from #10. Closes #10. --- doc/html/variant2.html | 26 ++++++++++++++++++-------- doc/variant2/reference.adoc | 14 ++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/doc/html/variant2.html b/doc/html/variant2.html index 9019e21..94c8f3a 100644 --- a/doc/html/variant2.html +++ b/doc/html/variant2.html @@ -1103,6 +1103,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…​.

+