From f4f949fc2f73c78c594b92f3dfd4a868db7fc9c3 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 2 Jun 2017 05:14:40 +0300 Subject: [PATCH 1/4] Add converting constructors, subset() --- include/boost/variant2/variant.hpp | 103 ++++++++++++++++++++++++++++- test/variant_copy_construct.cpp | 5 ++ test/variant_move_construct.cpp | 5 ++ 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 0df00df..cf41d4a 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -376,9 +376,10 @@ template struct overload: overload #if BOOST_WORKAROUND( BOOST_MSVC, <= 1910 ) -template struct resolve_overload_type_impl +template using resolve_overload_type_ = decltype( overload()(std::declval()) ); + +template struct resolve_overload_type_impl: mp_defer< resolve_overload_type_, U, T... > { - using type = decltype( overload()(std::declval()) ); }; template using resolve_overload_type = typename resolve_overload_type_impl::type::type; @@ -991,6 +992,104 @@ public: } using variant_base::_get_impl; + + // converting constructors (extension) + + template..., mp_contains, U>...>, void> > + variant( variant const& r ) + noexcept( mp_all...>::value ) + { + mp_for_index( r.index(), [&]( auto I ){ + + using J = mp_find, mp_at_c, I>>; + + ::new( static_cast(this) ) variant_base( J{}, r._get_impl( I ) ); + + }); + } + + template..., mp_contains, U>...>, void> > + variant( variant && r ) + noexcept( mp_all...>::value ) + { + mp_for_index( r.index(), [&]( auto I ){ + + using J = mp_find, mp_at_c, I>>; + + ::new( static_cast(this) ) variant_base( J{}, std::move( r._get_impl( I ) ) ); + + }); + } + + // subset (extension) + +private: + + template> static variant _subset_impl( mp_size_t, V && v ) + { + return variant( in_place_index, std::forward(v) ); + } + + template static variant _subset_impl( mp_size_t, V && /*v*/ ) + { + throw bad_variant_access(); + } + +public: + + template..., mp_contains, U>...>, void> > + constexpr variant subset() & + { + return mp_for_index( index(), [&]( auto I ){ + + using J = mp_find, mp_at_c, I>>; + + return _subset_impl( J{}, get( *this ) ); + + }); + } + + template..., mp_contains, U>...>, void> > + constexpr variant subset() const& + { + return mp_for_index( index(), [&]( auto I ){ + + using J = mp_find, mp_at_c, I>>; + + return _subset_impl( J{}, get( *this ) ); + + }); + } + + template..., mp_contains, U>...>, void> > + constexpr variant subset() && + { + return mp_for_index( index(), [&]( auto I ){ + + using J = mp_find, mp_at_c, I>>; + + return _subset_impl( J{}, get( std::move(*this) ) ); + + }); + } + + template..., mp_contains, U>...>, void> > + constexpr variant subset() const&& + { + return mp_for_index( index(), [&]( auto I ){ + + using J = mp_find, mp_at_c, I>>; + + return _subset_impl( J{}, get( std::move(*this) ) ); + + }); + } }; // relational operators diff --git a/test/variant_copy_construct.cpp b/test/variant_copy_construct.cpp index 473d8a2..6762776 100644 --- a/test/variant_copy_construct.cpp +++ b/test/variant_copy_construct.cpp @@ -121,7 +121,12 @@ int main() BOOST_TEST_TRAIT_FALSE((std::is_nothrow_copy_constructible>)); BOOST_TEST_TRAIT_TRUE((std::is_copy_constructible>)); + +#if !BOOST_WORKAROUND( BOOST_MSVC, <= 1910 ) + BOOST_TEST_TRAIT_FALSE((std::is_copy_constructible>)); + +#endif } return boost::report_errors(); diff --git a/test/variant_move_construct.cpp b/test/variant_move_construct.cpp index e3d8e98..4561ebd 100644 --- a/test/variant_move_construct.cpp +++ b/test/variant_move_construct.cpp @@ -122,7 +122,12 @@ int main() BOOST_TEST_TRAIT_FALSE((std::is_nothrow_move_constructible>)); BOOST_TEST_TRAIT_TRUE((std::is_move_constructible>)); + +#if !BOOST_WORKAROUND( BOOST_MSVC, <= 1910 ) + BOOST_TEST_TRAIT_FALSE((std::is_move_constructible>)); + +#endif } return boost::report_errors(); From 2a6c157fcc98fdb74e3acb2210496a2160693b80 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 2 Jun 2017 05:15:51 +0300 Subject: [PATCH 2/4] Add preliminary expected implementation --- include/boost/variant2/expected.hpp | 367 ++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 include/boost/variant2/expected.hpp diff --git a/include/boost/variant2/expected.hpp b/include/boost/variant2/expected.hpp new file mode 100644 index 0000000..f6651f3 --- /dev/null +++ b/include/boost/variant2/expected.hpp @@ -0,0 +1,367 @@ +#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 + +class bad_expected_access: public std::exception +{ +private: + + std::string msg_; + +public: + + bad_expected_access() noexcept + { + } + + template explicit bad_expected_access( mp_identity ) noexcept: msg_( "bad_expected_access: " + boost::core::demangle( typeid(E).name() ) ) + { + } + + char const * what() const noexcept + { + return msg_.empty()? "bad_expected_access": msg_.c_str(); + } +}; + +// expected + +template void throw_on_unexpected( E const & /*e*/ ) +{ + throw bad_expected_access( mp_identity() ); +} + +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( mp_identity() ); + } +} + +template class expected +{ +private: + + variant v_; + +private: + + void _bad_access() const + { + mp_for_index>( v_.index(), [&]( auto I ) + { + if( I == 0 ) + { + throw bad_expected_access( mp_identity() ); + } + else + { + throw_on_unexpected( get(v_) ); + } + }); + } + +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_for_index>( v_.index(), [&]( auto I ) { + + return _remap_error( I, f, get(v_) ); + + }); + } + + expected remap_errors() + { + using R = expected; + + auto f = []( auto const& e ){ return make_error_code(e); }; + + return mp_for_index>( v_.index(), [&]( auto I ) { + + return _remap_error( I, f, get(v_) ); + + }); + } +}; + +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 c99bf2205cc3f23e5310ea97f18db6eb711e7410 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 2 Jun 2017 05:41:13 +0300 Subject: [PATCH 3/4] Move index() into variant_base to avoid needless ix_ >= 0 checks when single buffered --- include/boost/variant2/variant.hpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index cf41d4a..9f3a980 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -413,6 +413,11 @@ template struct variant_base_impl { } + constexpr std::size_t index() const noexcept + { + return ix_ - 1; + } + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -486,6 +491,11 @@ template struct variant_base_impl { } + constexpr std::size_t index() const noexcept + { + return ix_ >= 0? ix_ - 1: -ix_ - 1; + } + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -555,6 +565,11 @@ template struct variant_base_impl _destroy(); } + constexpr std::size_t index() const noexcept + { + return ix_ - 1; + } + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -661,6 +676,11 @@ template struct variant_base_impl _destroy(); } + constexpr std::size_t index() const noexcept + { + return ix_ >= 0? ix_ - 1: -ix_ - 1; + } + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -958,10 +978,7 @@ public: // value status - constexpr size_t index() const noexcept - { - return this->ix_ >= 0? this->ix_ - 1 : -this->ix_ - 1; - } + using variant_base::index; // swap From d55db1dbc6c59c9f17314810d7c0d34b67f5bada Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 2 Jun 2017 06:02:50 +0300 Subject: [PATCH 4/4] Replace use of get<> with ._get_impl for better codegen --- include/boost/variant2/variant.hpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 9f3a980..9c6b47d 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -892,11 +892,11 @@ public: if( this->index() == I ) { - get(*this) = get(r); + this->_get_impl( I ) = r._get_impl( I ); } else { - this->variant_base::template emplace( get(r) ); + this->variant_base::template emplace( r._get_impl( I ) ); } }); @@ -912,11 +912,11 @@ public: if( this->index() == I ) { - get(*this) = get(std::move(r)); + this->_get_impl( I ) = std::move( r._get_impl( I ) ); } else { - this->variant_base::template emplace( get(std::move(r)) ); + this->variant_base::template emplace( std::move( r._get_impl( I ) ) ); } }); @@ -989,7 +989,7 @@ public: mp_for_index( index(), [&]( auto I ){ using std::swap; - swap( get(*this), get(r) ); + swap( this->_get_impl( I ), r._get_impl( I ) ); }); } @@ -1064,7 +1064,7 @@ public: using J = mp_find, mp_at_c, I>>; - return _subset_impl( J{}, get( *this ) ); + return _subset_impl( J{}, this->_get_impl( I ) ); }); } @@ -1077,7 +1077,7 @@ public: using J = mp_find, mp_at_c, I>>; - return _subset_impl( J{}, get( *this ) ); + return _subset_impl( J{}, this->_get_impl( I ) ); }); } @@ -1090,7 +1090,7 @@ public: using J = mp_find, mp_at_c, I>>; - return _subset_impl( J{}, get( std::move(*this) ) ); + return _subset_impl( J{}, std::move( this->_get_impl( I ) ) ); }); } @@ -1103,7 +1103,7 @@ public: using J = mp_find, mp_at_c, I>>; - return _subset_impl( J{}, get( std::move(*this) ) ); + return _subset_impl( J{}, std::move( this->_get_impl( I ) ) ); }); } @@ -1116,7 +1116,7 @@ template constexpr bool operator==( variant const & v, variant return mp_for_index( v.index(), [&]( auto I ){ - return get(v) == get(w); + return v._get_impl( I ) == w._get_impl( I ); }); } @@ -1127,7 +1127,7 @@ template constexpr bool operator!=( variant const & v, variant return mp_for_index( v.index(), [&]( auto I ){ - return get(v) != get(w); + return v._get_impl( I ) != w._get_impl( I ); }); } @@ -1139,7 +1139,7 @@ template constexpr bool operator<( variant const & v, variant< return mp_for_index( v.index(), [&]( auto I ){ - return get(v) < get(w); + return v._get_impl( I ) < w._get_impl( I ); }); } @@ -1151,7 +1151,7 @@ template constexpr bool operator>( variant const & v, variant return mp_for_index( v.index(), [&]( auto I ){ - return get(v) > get(w); + return v._get_impl( I ) > w._get_impl( I ); }); } @@ -1163,7 +1163,7 @@ template constexpr bool operator<=( variant const & v, variant return mp_for_index( v.index(), [&]( auto I ){ - return get(v) <= get(w); + return v._get_impl( I ) <= w._get_impl( I ); }); } @@ -1175,7 +1175,7 @@ template constexpr bool operator>=( variant const & v, variant return mp_for_index( v.index(), [&]( auto I ){ - return get(v) >= get(w); + return v._get_impl( I ) >= w._get_impl( I ); }); }