From c891c8e2051613e74a90ff67cea9f4c63ac8e98e Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 1 Jun 2017 17:27:17 +0300 Subject: [PATCH 1/6] Do not include boost/mp11.hpp if already included, to allow use on Compiler Explorer --- include/boost/variant2/variant.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 4ce586e..f262f0e 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -8,7 +8,9 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt +#ifndef BOOST_MP11_HPP_INCLUDED #include +#endif #include #include #include From 77e8c460747d949d5772fa6b576c946e3c7a5c65 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 1 Jun 2017 19:24:50 +0300 Subject: [PATCH 2/6] Fix return type of visit() to properly take cv& into account --- include/boost/variant2/variant.hpp | 55 +++++++++++++++++++++--------- test/variant_visit.cpp | 26 +++++++++++++- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index f262f0e..5b319b4 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -81,22 +81,22 @@ template using variant_alternative_t = typename variant_ namespace detail { - template using var_alt_t = mp_invoke>; + template using var_alt_impl = mp_invoke>; } // namespace detail template struct variant_alternative { }; -template struct variant_alternative: mp_defer< variant2::detail::var_alt_t, mp_size_t, T, mp_quote> +template struct variant_alternative: mp_defer< variant2::detail::var_alt_impl, mp_size_t, T, mp_quote> { }; -template struct variant_alternative: mp_defer< variant2::detail::var_alt_t, mp_size_t, T, mp_quote> +template struct variant_alternative: mp_defer< variant2::detail::var_alt_impl, mp_size_t, T, mp_quote> { }; -template struct variant_alternative: mp_defer< variant2::detail::var_alt_t, mp_size_t, T, mp_quote> +template struct variant_alternative: mp_defer< variant2::detail::var_alt_impl, mp_size_t, T, mp_quote> { }; @@ -1065,16 +1065,9 @@ template constexpr bool operator>=( variant const & v, variant } // visitation -template constexpr auto visit( F&& f ) -> decltype(std::forward(f)()) -{ - return std::forward(f)(); -} - namespace detail { -template using remove_cv_ref = std::remove_cv_t>; - template struct Qret { template using fn = decltype( std::declval()( std::declval()... ) ); @@ -1082,13 +1075,41 @@ template struct Qret template using front_if_same = mp_if, mp_front>; -template using Vret = front_if_same, remove_cv_ref...>>; +template using var_size = variant_size>; + +template>> using apply_cv_ref_ = mp_if, T&, T>; + +#if BOOST_WORKAROUND( BOOST_MSVC, <= 1910 ) + +template struct apply_cv_ref_impl +{ + template using _f = apply_cv_ref_; + + using L = mp_iota>; + + using type = mp_transform<_f, L>; +}; + +template using apply_cv_ref = typename apply_cv_ref_impl::type; + +#else + +template using apply_cv_ref = mp_transform_q, mp_iota>>; + +#endif + +template using Vret = front_if_same, apply_cv_ref...>>; } // namespace detail +template constexpr auto visit( F&& f ) -> decltype(std::forward(f)()) +{ + return std::forward(f)(); +} + template constexpr auto visit( F&& f, V1&& v1 ) -> variant2::detail::Vret { - return mp_for_index>>( v1.index(), [&]( auto I ){ + return mp_for_index>( v1.index(), [&]( auto I ){ return std::forward(f)( get( std::forward(v1) ) ); @@ -1099,7 +1120,7 @@ template constexpr auto visit( F&& f, V1&& v1 ) -> variant2:: template constexpr auto visit( F&& f, V1&& v1, V2&& v2 ) -> variant2::detail::Vret { - return mp_for_index>>( v1.index(), [&]( auto I ){ + return mp_for_index>( v1.index(), [&]( auto I ){ auto f2 = [&]( auto&&... a ){ return std::forward(f)( get( std::forward(v1) ), std::forward(a)... ); }; return visit( f2, std::forward(v2) ); @@ -1109,7 +1130,7 @@ template constexpr auto visit( F&& f, V1&& v1, V2&& template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V3&& v3 ) -> variant2::detail::Vret { - return mp_for_index>>( v1.index(), [&]( auto I ){ + return mp_for_index>( v1.index(), [&]( auto I ){ auto f2 = [&]( auto&&... a ){ return std::forward(f)( get( std::forward(v1) ), std::forward(a)... ); }; return visit( f2, std::forward(v2), std::forward(v3) ); @@ -1119,7 +1140,7 @@ template constexpr auto visit( F&& f, V1& template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V3&& v3, V4&& v4 ) -> variant2::detail::Vret { - return mp_for_index>>( v1.index(), [&]( auto I ){ + return mp_for_index>( v1.index(), [&]( auto I ){ auto f2 = [&]( auto&&... a ){ return std::forward(f)( get( std::forward(v1) ), std::forward(a)... ); }; return visit( f2, std::forward(v2), std::forward(v3), std::forward(v4) ); @@ -1131,7 +1152,7 @@ template constexpr auto visit( template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V&&... v ) -> variant2::detail::Vret { - return mp_for_index>>( v1.index(), [&]( auto I ){ + return mp_for_index>( v1.index(), [&]( auto I ){ auto f2 = [&]( auto&&... a ){ return std::forward(f)( get( std::forward(v1) ), std::forward(a)... ); }; return visit( f2, std::forward(v2), std::forward(v)... ); diff --git a/test/variant_visit.cpp b/test/variant_visit.cpp index 5058e3e..98fdb29 100644 --- a/test/variant_visit.cpp +++ b/test/variant_visit.cpp @@ -7,6 +7,7 @@ // http://www.boost.org/LICENSE_1_0.txt #include +#include #include #include #include @@ -15,6 +16,19 @@ #include using namespace boost::variant2; +using boost::mp11::mp_size_t; + +struct X +{ +}; + +struct F +{ + mp_size_t<1> operator()( X& ) const; + mp_size_t<2> operator()( X const& ) const; + mp_size_t<3> operator()( X&& ) const; + mp_size_t<4> operator()( X const&& ) const; +}; int main() { @@ -84,7 +98,17 @@ int main() BOOST_TEST_EQ( (visit( []( auto x1, auto x2, auto x3, auto x4 ){ return (long long)(x1 * 100) * 100000000 + (long long)(x2 * 100) * 100000 + (long long)(x3 * 10000) + (int)x4; }, v1, v2, v3, v4 )), 10031462800 + 'A' ); visit( []( auto x1, auto x2, auto x3, auto x4 ){ BOOST_TEST_EQ( x1, 1 ); BOOST_TEST_EQ( x2, 3.14f ); BOOST_TEST_EQ( x3, 6.28 ); BOOST_TEST_EQ( x4, 'A' ); }, v1, v2, v3, v4 ); - visit( []( auto x1, auto x2, auto x3, auto x4 ){ BOOST_TEST_EQ( x1, 1 ); BOOST_TEST_EQ( x2, 3.14f ); BOOST_TEST_EQ( x3, 6.28 ); BOOST_TEST_EQ( x4, 'A' ); }, std::move(v1), std::move(v2), std::move(v3), std::move(v4) ); + visit( []( auto x1, auto x2, auto x3, auto x4 ){ BOOST_TEST_EQ( x1, 1 ); BOOST_TEST_EQ( x2, 3.14f ); BOOST_TEST_EQ( x3, 6.28 ); BOOST_TEST_EQ( x4, 'A' ); }, std::move(v1), std::move(v2), std::move(v3), std::move(v4) ); + } + + { + variant v; + variant const cv; + + BOOST_TEST_EQ( decltype(visit(F{}, v))::value, 1 ); + BOOST_TEST_EQ( decltype(visit(F{}, cv))::value, 2 ); + BOOST_TEST_EQ( decltype(visit(F{}, std::move(v)))::value, 3 ); + BOOST_TEST_EQ( decltype(visit(F{}, std::move(cv)))::value, 4 ); } return boost::report_errors(); From 6a45fa56adb1b4c51e81ea1c2a24d0e7719df863 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 1 Jun 2017 21:18:57 +0300 Subject: [PATCH 3/6] Fix copyright --- include/boost/variant2/variant.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 5b319b4..0df00df 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_VARIANT2_VARIANT_HPP_INCLUDED #define BOOST_VARIANT2_VARIANT_HPP_INCLUDED -// Copyright 2015-2017 Peter Dimov. +// Copyright 2017 Peter Dimov. // // Distributed under the Boost Software License, Version 1.0. // From ca14504675523f1ed6adc5a8750c4e075bea4157 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 1 Jun 2017 21:22:19 +0300 Subject: [PATCH 4/6] Add preliminary result.hpp --- include/boost/variant2/result.hpp | 226 ++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 include/boost/variant2/result.hpp diff --git a/include/boost/variant2/result.hpp b/include/boost/variant2/result.hpp new file mode 100644 index 0000000..679c069 --- /dev/null +++ b/include/boost/variant2/result.hpp @@ -0,0 +1,226 @@ +#ifndef BOOST_VARIANT2_RESULT_HPP_INCLUDED +#define BOOST_VARIANT2_RESULT_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 + +// + +namespace boost +{ +namespace variant2 +{ + +enum class result_errc +{ + not_initialized +}; + +class result_error_category: public std::error_category +{ +public: + + virtual const char * name() const noexcept + { + return "boost::variant2::result"; + } + + virtual std::string message( int e ) const + { + switch( e ) + { + case (int)result_errc::not_initialized: + + return "result<> not initialized"; + + default: + + return "unknown result<> error"; + } + } + + static result_error_category const & instance() + { + static result_error_category cat; + return cat; + } +}; + +std::error_code make_error_code( result_errc e ) +{ + return std::error_code( static_cast( e ), result_error_category::instance() ); +} + +template class result +{ +private: + + variant v_; + +public: + + // constructors + + constexpr result() noexcept: v_( make_error_code( result_errc::not_initialized ) ) + { + } + + constexpr result( T const& t ): v_( t ) + { + } + + constexpr result( T&& t ): v_( std::move(t) ) + { + } + + constexpr result( std::error_code const & ec ) noexcept: v_( ec ) + { + } + + // queries + + constexpr bool has_value() const noexcept + { + return v_.index() == 0; + } + + constexpr bool has_error() const noexcept + { + return v_.index() == 1; + } + + constexpr explicit operator bool() const noexcept + { + return v_.index() == 0; + } + + // checked value access + + constexpr T& value() & + { + if( has_value() ) + { + return *get_if<0>(&v_); + } + else + { + throw std::system_error( *get_if<1>(&v_) ); + } + } + + constexpr T const& value() const& + { + if( has_value() ) + { + return *get_if<0>(&v_); + } + else + { + throw std::system_error( *get_if<1>(&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 access + + /*constexpr*/ std::error_code error() const + { + if( has_error() ) + { + return *get_if<1>(&v_); + } + else + { + return std::error_code(); + } + } + + // setters + + void set_value( T const& t ) noexcept( std::is_nothrow_copy_constructible::value ) + { + v_.emplace<0>( t ); + } + + void set_value( T&& t ) noexcept( std::is_nothrow_move_constructible::value ) + { + v_.emplace<0>( std::move( t ) ); + } + + void set_error( std::error_code const & e ) noexcept + { + v_.emplace<1>( e ); + } + + // swap + + void swap( result& r ) noexcept( noexcept( v_.swap( r.v_ ) ) ) + { + v_.swap( r.v_ ); + } +}; + +} // namespace variant2 +} // namespace boost + +#endif // #ifndef BOOST_VARIANT2_RESULT_HPP_INCLUDED From 3a7078cdfbee3f2db89afc458ca27bc369ca8681 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 1 Jun 2017 21:47:40 +0300 Subject: [PATCH 5/6] Add preliminary outcome.hpp --- include/boost/variant2/outcome.hpp | 259 +++++++++++++++++++++++++++++ include/boost/variant2/result.hpp | 2 +- 2 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 include/boost/variant2/outcome.hpp diff --git a/include/boost/variant2/outcome.hpp b/include/boost/variant2/outcome.hpp new file mode 100644 index 0000000..4ebbaa5 --- /dev/null +++ b/include/boost/variant2/outcome.hpp @@ -0,0 +1,259 @@ +#ifndef BOOST_VARIANT2_OUTCOME_HPP_INCLUDED +#define BOOST_VARIANT2_OUTCOME_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 + +// + +namespace boost +{ +namespace variant2 +{ + +enum class outcome_errc +{ + not_initialized +}; + +class outcome_error_category: public std::error_category +{ +public: + + virtual const char * name() const noexcept + { + return "boost::variant2::outcome"; + } + + virtual std::string message( int e ) const + { + switch( e ) + { + case (int)outcome_errc::not_initialized: + + return "outcome<> not initialized"; + + default: + + return "unknown outcome<> error"; + } + } + + static outcome_error_category const & instance() + { + static outcome_error_category cat; + return cat; + } +}; + +std::error_code make_error_code( outcome_errc e ) +{ + return std::error_code( static_cast( e ), outcome_error_category::instance() ); +} + +template class outcome +{ +private: + + variant v_; + +public: + + // constructors + + constexpr outcome() noexcept: v_( make_error_code( outcome_errc::not_initialized ) ) + { + } + + constexpr outcome( T const& t ): v_( t ) + { + } + + constexpr outcome( T&& t ): v_( std::move(t) ) + { + } + + constexpr outcome( std::error_code const & ec ) noexcept: v_( ec ) + { + } + + constexpr outcome( std::exception_ptr const & ep ) noexcept: v_( ep ) + { + } + + // queries + + constexpr bool has_value() const noexcept + { + return v_.index() == 0; + } + + constexpr bool has_error() const noexcept + { + return v_.index() == 1; + } + + constexpr bool has_exception() const noexcept + { + return v_.index() == 2; + } + + constexpr explicit operator bool() const noexcept + { + return v_.index() == 0; + } + + // checked value access + + constexpr T& value() & + { + if( has_value() ) + { + return *get_if<0>(&v_); + } + else + { + throw std::system_error( *get_if<1>(&v_) ); + } + } + + constexpr T const& value() const& + { + if( has_value() ) + { + return *get_if<0>(&v_); + } + else + { + throw std::system_error( *get_if<1>(&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 access + + /*constexpr*/ std::error_code error() const noexcept + { + if( has_error() ) + { + return *get_if<1>(&v_); + } + else + { + return std::error_code(); + } + } + + // exception access + + std::exception_ptr exception() const noexcept + { + if( has_exception() ) + { + return *get_if<2>(&v_); + } + else if( has_value() ) + { + return std::exception_ptr(); + } + else + { + return std::make_exception_ptr( std::system_error( *get_if<1>(&v_) ) ); + } + } + + // setters + + void set_value( T const& t ) noexcept( std::is_nothrow_copy_constructible::value ) + { + v_.emplace<0>( t ); + } + + void set_value( T&& t ) noexcept( std::is_nothrow_move_constructible::value ) + { + v_.emplace<0>( std::move( t ) ); + } + + void set_error( std::error_code const & e ) noexcept + { + v_.emplace<1>( e ); + } + + void set_exception( std::exception_ptr const & x ) noexcept + { + v_.emplace<2>( x ); + } + + // swap + + void swap( outcome& r ) noexcept( noexcept( v_.swap( r.v_ ) ) ) + { + v_.swap( r.v_ ); + } +}; + +} // namespace variant2 +} // namespace boost + +#endif // #ifndef BOOST_VARIANT2_OUTCOME_HPP_INCLUDED diff --git a/include/boost/variant2/result.hpp b/include/boost/variant2/result.hpp index 679c069..72f72b1 100644 --- a/include/boost/variant2/result.hpp +++ b/include/boost/variant2/result.hpp @@ -183,7 +183,7 @@ public: // error access - /*constexpr*/ std::error_code error() const + /*constexpr*/ std::error_code error() const noexcept { if( has_error() ) { From 26c1f716ec0f800d5fe575f5eb6082d788736d4b Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 2 Jun 2017 00:44:20 +0300 Subject: [PATCH 6/6] Add test/variant_lt_gt --- test/Jamfile | 1 + test/variant_lt_gt.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 test/variant_lt_gt.cpp diff --git a/test/Jamfile b/test/Jamfile index 4a68bfe..3dccee1 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -38,3 +38,4 @@ run variant_swap.cpp : : : $(REQ) ; run variant_eq_ne.cpp : : : $(REQ) ; run variant_destroy.cpp : : : $(REQ) ; run variant_visit.cpp : : : $(REQ) ; +run variant_lt_gt.cpp : : : $(REQ) ; diff --git a/test/variant_lt_gt.cpp b/test/variant_lt_gt.cpp new file mode 100644 index 0000000..558052c --- /dev/null +++ b/test/variant_lt_gt.cpp @@ -0,0 +1,85 @@ + +// 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 + +#include +#include +#include +#include +#include +#include + +using namespace boost::variant2; + +struct X +{ +}; + +inline bool operator<( X const&, X const& ) { return false; } +inline bool operator>( X const&, X const& ) { return false; } +inline bool operator<=( X const&, X const& ) { return false; } +inline bool operator>=( X const&, X const& ) { return false; } + +#define TEST_EQ( v1, v2 ) \ + BOOST_TEST_NOT( v1 < v2 ); \ + BOOST_TEST_NOT( v1 > v2 ); \ + BOOST_TEST( v1 <= v2 ); \ + BOOST_TEST( v1 >= v2 ); + +#define TEST_LE( v1, v3 ) \ + BOOST_TEST( v1 < v3 ); \ + BOOST_TEST( v3 > v1 ); \ + BOOST_TEST_NOT( v1 > v3 ); \ + BOOST_TEST_NOT( v3 < v1 ); \ + BOOST_TEST( v1 <= v3 ); \ + BOOST_TEST( v3 >= v1 ); \ + BOOST_TEST_NOT( v1 >= v3 ); \ + BOOST_TEST_NOT( v3 <= v1 ); + +int main() +{ + { + variant v1, v2, v3( 1 ), v4( 1 ); + + TEST_EQ( v1, v2 ) + TEST_LE( v1, v3 ) + TEST_EQ( v3, v4 ) + } + + { + variant v1, v2, v3( 1 ), v4( 1 ), v5( 3.14f ), v6( 3.14f ); + + TEST_EQ( v1, v2 ) + TEST_LE( v1, v3 ) + TEST_EQ( v3, v4 ) + TEST_LE( v1, v5 ) + TEST_LE( v3, v5 ) + TEST_EQ( v5, v6 ) + } + + { + variant v1, v2, v3( in_place_index<1> ), v4( in_place_index<1> ), v5( 3.14f ), v6( 3.14f ); + + TEST_EQ( v1, v2 ) + TEST_LE( v1, v3 ) + TEST_EQ( v3, v4 ) + TEST_LE( v1, v5 ) + TEST_LE( v3, v5 ) + TEST_EQ( v5, v6 ) + } + + { + variant v1, v2; + + BOOST_TEST_NOT( v1 < v2 ); + BOOST_TEST_NOT( v1 > v2 ); + BOOST_TEST_NOT( v1 <= v2 ); + BOOST_TEST_NOT( v1 >= v2 ); + } + + return boost::report_errors(); +}