From 5586ebaa64a04ccbd66d1b863df2a90b3cb0f2e0 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Wed, 3 Jun 2020 17:00:13 +0300 Subject: [PATCH] Add support for visit --- include/boost/variant2/variant.hpp | 61 ++++++++++++------- test/Jamfile | 2 + test/variant_visit_r.cpp | 98 ++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 test/variant_visit_r.cpp diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 5c66176..7a4a2cf 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -2009,11 +2009,26 @@ template using front_if_same = mp11::mp_if using apply_cv_ref = mp11::mp_product, mp11::mp_list>; -template using Vret = front_if_same, apply_cv_ref...>>; +struct deduced {}; + +#if !BOOST_WORKAROUND( BOOST_MSVC, < 1920 ) + +template using Vret = mp11::mp_eval_if_not< std::is_same, R, front_if_same, mp11::mp_product_q, apply_cv_ref...> >; + +#else + +template struct Vret_impl +{ + using type = mp11::mp_eval_if_not< std::is_same, R, front_if_same, mp11::mp_product_q, apply_cv_ref...> >; +}; + +template using Vret = typename Vret_impl::type; + +#endif } // namespace detail -template constexpr auto visit( F&& f ) -> decltype(std::forward(f)()) +template constexpr auto visit( F&& f ) -> detail::Vret { return std::forward(f)(); } @@ -2021,12 +2036,12 @@ template constexpr auto visit( F&& f ) -> decltype(std::forward(f)() namespace detail { -template struct visit_L1 +template struct visit_L1 { F&& f; V1&& v1; - template auto operator()( I ) const -> Vret + template auto operator()( I ) const -> Vret { return std::forward(f)( unsafe_get( std::forward(v1) ) ); } @@ -2034,9 +2049,9 @@ template struct visit_L1 } // namespace detail -template constexpr auto visit( F&& f, V1&& v1 ) -> detail::Vret +template constexpr auto visit( F&& f, V1&& v1 ) -> detail::Vret { - return mp11::mp_with_index>( v1.index(), detail::visit_L1{ std::forward(f), std::forward(v1) } ); + return mp11::mp_with_index>( v1.index(), detail::visit_L1{ std::forward(f), std::forward(v1) } ); } #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) || BOOST_WORKAROUND( BOOST_MSVC, < 1920 ) @@ -2060,31 +2075,31 @@ template bind_front_ bind_front( F&& f, A&& a ) return bind_front_{ std::forward(f), std::forward(a) }; } -template struct visit_L2 +template struct visit_L2 { F&& f; V1&& v1; V2&& v2; - template auto operator()( I ) const -> Vret + template auto operator()( I ) const -> Vret { auto f2 = bind_front( std::forward(f), unsafe_get( std::forward(v1) ) ); - return visit( f2, std::forward(v2) ); + return visit( f2, std::forward(v2) ); } }; } // namespace detail -template constexpr auto visit( F&& f, V1&& v1, V2&& v2 ) -> detail::Vret +template constexpr auto visit( F&& f, V1&& v1, V2&& v2 ) -> detail::Vret { - return mp11::mp_with_index>( v1.index(), detail::visit_L2{ std::forward(f), std::forward(v1), std::forward(v2) } ); + return mp11::mp_with_index>( v1.index(), detail::visit_L2{ std::forward(f), std::forward(v1), std::forward(v2) } ); } namespace detail { -template struct visit_L3 +template struct visit_L3 { F&& f; @@ -2092,24 +2107,24 @@ template struct visit_L3 V2&& v2; V3&& v3; - template auto operator()( I ) const -> Vret + template auto operator()( I ) const -> Vret { auto f2 = bind_front( std::forward(f), unsafe_get( std::forward(v1) ) ); - return visit( f2, std::forward(v2), std::forward(v3) ); + return visit( f2, std::forward(v2), std::forward(v3) ); } }; } // namespace detail -template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V3&& v3 ) -> detail::Vret +template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V3&& v3 ) -> detail::Vret { - return mp11::mp_with_index>( v1.index(), detail::visit_L3{ std::forward(f), std::forward(v1), std::forward(v2), std::forward(v3) } ); + return mp11::mp_with_index>( v1.index(), detail::visit_L3{ std::forward(f), std::forward(v1), std::forward(v2), std::forward(v3) } ); } namespace detail { -template struct visit_L4 +template struct visit_L4 { F&& f; @@ -2118,28 +2133,28 @@ template struct visit_L4 V3&& v3; V4&& v4; - template auto operator()( I ) const -> Vret + template auto operator()( I ) const -> Vret { auto f2 = bind_front( std::forward(f), unsafe_get( std::forward(v1) ) ); - return visit( f2, std::forward(v2), std::forward(v3), std::forward(v4) ); + return visit( f2, std::forward(v2), std::forward(v3), std::forward(v4) ); } }; } // namespace detail -template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V3&& v3, V4&& v4 ) -> detail::Vret +template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V3&& v3, V4&& v4 ) -> detail::Vret { - return mp11::mp_with_index>( v1.index(), detail::visit_L4{ std::forward(f), std::forward(v1), std::forward(v2), std::forward(v3), std::forward(v4) } ); + return mp11::mp_with_index>( v1.index(), detail::visit_L4{ std::forward(f), std::forward(v1), std::forward(v2), std::forward(v3), std::forward(v4) } ); } #else -template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V&&... v ) -> detail::Vret +template constexpr auto visit( F&& f, V1&& v1, V2&& v2, V&&... v ) -> detail::Vret { return mp11::mp_with_index>( v1.index(), [&]( auto I ){ auto f2 = [&]( auto&&... a ){ return std::forward(f)( detail::unsafe_get( std::forward(v1) ), std::forward(a)... ); }; - return visit( f2, std::forward(v2), std::forward(v)... ); + return visit( f2, std::forward(v2), std::forward(v)... ); }); } diff --git a/test/Jamfile b/test/Jamfile index fdb6024..742b2ad 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -114,3 +114,5 @@ run variant_special.cpp ; run variant_visit_derived.cpp ; run variant_many_types.cpp ; + +run variant_visit_r.cpp ; diff --git a/test/variant_visit_r.cpp b/test/variant_visit_r.cpp new file mode 100644 index 0000000..15ab60a --- /dev/null +++ b/test/variant_visit_r.cpp @@ -0,0 +1,98 @@ + +// 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 + +#if defined(_MSC_VER) +# pragma warning( disable: 4244 ) // conversion from float to int, possible loss of data +#endif + +#include +#include + +using namespace boost::variant2; + +struct F1 +{ + template T1 operator()( T1 t1 ) const + { + return t1; + } +}; + +struct F2 +{ + template auto operator()( T1 t1, T2 t2 ) const -> decltype( t1 + t2 ) + { + return t1 + t2; + } +}; + +struct F3 +{ + template auto operator()( T1 t1, T2 t2, T3 t3 ) const -> decltype( t1 + t2 + t3 ) + { + return t1 + t2 + t3; + } +}; + +struct F4 +{ + template auto operator()( T1 t1, T2 t2, T3 t3, T4 t4 ) const -> decltype( t1 + t2 + t3 + t4 ) + { + return t1 + t2 + t3 + t4; + } +}; + +int main() +{ + { + BOOST_TEST_EQ( (visit( []{ return 3.14f; } )), 3 ); + } + + { + variant v( 1 ); + + BOOST_TEST_EQ( visit( F1(), v ), 1 ); + BOOST_TEST_EQ( visit( F1(), v ), 1.0f ); + } + + { + variant const v( 3.14f ); + + BOOST_TEST_EQ( visit( F1(), v ), 3 ); + BOOST_TEST_EQ( visit( F1(), v ), 3.14f ); + } + + { + variant v1( 1 ); + variant const v2( 3.14f ); + + BOOST_TEST_EQ( visit( F2(), v1, v2 ), 4 ); + BOOST_TEST_EQ( visit( F2(), v1, v2 ), 1 + 3.14f ); + } + + { + variant v1( 1 ); + variant const v2( 3.14f ); + variant v3( 6.28 ); + + BOOST_TEST_EQ( visit( F3(), v1, v2, v3 ), 10 ); + BOOST_TEST_EQ( visit( F3(), v1, v2, v3 ), static_cast( 1 + 3.14f + 6.28 ) ); + } + + { + variant v1( 1 ); + variant const v2( 3.14f ); + variant v3( 6.28 ); + variant const v4( 'A' ); + + BOOST_TEST_EQ( visit( F4(), v1, v2, v3, v4 ), 10 + 'A' ); + BOOST_TEST_EQ( visit( F4(), v1, v2, v3, v4 ), static_cast( 1 + 3.14f + 6.28 + 'A' ) ); + } + + return boost::report_errors(); +}