From 0997734e5cfd1ae083d706b88af8d640f227b46f Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 9 Jun 2017 02:05:34 +0300 Subject: [PATCH] Make emplace constexpr --- include/boost/variant2/variant.hpp | 73 +++++++++++++++++--------- test/Jamfile | 2 + test/variant_emplace_index_cx.cpp | 83 ++++++++++++++++++++++++++++++ test/variant_emplace_type_cx.cpp | 63 +++++++++++++++++++++++ 4 files changed, 196 insertions(+), 25 deletions(-) create mode 100644 test/variant_emplace_index_cx.cpp create mode 100644 test/variant_emplace_type_cx.cpp diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 4a0937c..ee457c9 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -342,12 +342,22 @@ template union variant_storage_impl { } - template void emplace( mp_size_t<0>, A&&... a ) noexcept + template void emplace_impl( mp_false, A&&... a ) noexcept { ::new( &first_ ) T1( std::forward(a)... ); } - template void emplace( mp_size_t, A&&... a ) noexcept + template constexpr void emplace_impl( mp_true, A&&... a ) noexcept + { + *this = variant_storage_impl( mp_size_t<0>(), std::forward(a)... ); + } + + template constexpr void emplace( mp_size_t<0>, A&&... a ) noexcept + { + this->emplace_impl( mp_all, std::is_trivially_move_assignable...>(), std::forward(a)... ); + } + + template constexpr void emplace( mp_size_t, A&&... a ) noexcept { rest_.emplace( mp_size_t(), std::forward(a)... ); } @@ -436,20 +446,25 @@ template struct variant_base_impl return st1_.get( mp_size_t() ); } - template void emplace( A&&... a ) + template constexpr void emplace_impl( mp_true, mp_bool, A&&... a ) { - std::size_t const J = I+1; + st1_.emplace( mp_size_t(), std::forward(a)... ); + ix_ = J; + } + template constexpr void emplace_impl( mp_false, mp_true, A&&... a ) + { + U tmp( std::forward(a)... ); + + st1_.emplace( mp_size_t(), std::move(tmp) ); + ix_ = J; + } + + template void emplace_impl( mp_false, mp_false, A&&... a ) + { std::size_t const K = mp_find_if, std::is_nothrow_constructible>::value; - using U = mp_at_c, I>; - - if( std::is_nothrow_constructible::value ) - { - st1_.emplace( mp_size_t(), std::forward(a)... ); - ix_ = J; - } - else if( K < sizeof...(T) ) // have nothrow destructible + if( K < sizeof...(T) ) // have nothrow destructible { try { @@ -458,8 +473,8 @@ template struct variant_base_impl } catch( ... ) { - st1_.emplace( mp_size_t() ); - ix_ = K; + st1_.emplace( mp_size_t() ); + ix_ = K+1; throw; } @@ -474,6 +489,14 @@ template struct variant_base_impl ix_ = J; } } + + template constexpr void emplace( A&&... a ) + { + std::size_t const J = I+1; + using U = mp_at_c, I>; + + this->emplace_impl( std::is_nothrow_constructible(), mp_all, std::is_trivially_move_assignable...>(), std::forward(a)... ); + } }; // trivially destructible, double buffered @@ -516,7 +539,7 @@ template struct variant_base_impl return ix_ >= 0? st1_.get( j ): st2_.get( j ); } - template void emplace( A&&... a ) + template constexpr void emplace( A&&... a ) { size_t const J = I+1; @@ -830,7 +853,7 @@ public: } template...>, E1>> - variant( variant const& r ) + constexpr variant( variant const& r ) noexcept( mp_all...>::value ) { mp_with_index( r.index(), [&]( auto I ){ @@ -841,7 +864,7 @@ public: } template...>, E1>> - variant( variant && r ) + constexpr variant( variant && r ) noexcept( mp_all...>::value ) { mp_with_index( r.index(), [&]( auto I ){ @@ -885,7 +908,7 @@ public: // assignment template..., std::is_copy_assignable...>, E1>> - variant& operator=( variant const & r ) + constexpr variant& operator=( variant const & r ) noexcept( mp_all..., std::is_nothrow_copy_assignable...>::value ) { mp_with_index( r.index(), [&]( auto I ){ @@ -905,7 +928,7 @@ public: } template..., std::is_move_assignable...>, E1>> - variant& operator=( variant && r ) + constexpr variant& operator=( variant && r ) noexcept( mp_all..., std::is_nothrow_move_assignable...>::value ) { mp_with_index( r.index(), [&]( auto I ){ @@ -929,7 +952,7 @@ public: class V = variant2::detail::resolve_overload_type, class E2 = std::enable_if_t::value && std::is_constructible::value> > - variant& operator=( U&& u ) + constexpr variant& operator=( U&& u ) noexcept( std::is_nothrow_assignable::value && std::is_nothrow_constructible::value ) { std::size_t const I = variant2::detail::resolve_overload_index::value; @@ -949,28 +972,28 @@ public: // modifiers template, U>, class E = std::enable_if_t::value>> - U& emplace( A&&... a ) + constexpr U& emplace( A&&... a ) { variant_base::template emplace( std::forward(a)... ); return _get_impl( I() ); } template, U>, class E = std::enable_if_t&, A...>::value>> - U& emplace( std::initializer_list il, A&&... a ) + constexpr U& emplace( std::initializer_list il, A&&... a ) { variant_base::template emplace( il, std::forward(a)... ); return _get_impl( I() ); } template, I>, A...>::value>> - variant_alternative_t>& emplace( A&&... a ) + constexpr variant_alternative_t>& emplace( A&&... a ) { variant_base::template emplace( std::forward(a)... ); return _get_impl( mp_size_t() ); } template, I>, std::initializer_list&, A...>::value>> - variant_alternative_t>& emplace( std::initializer_list il, A&&... a ) + constexpr variant_alternative_t>& emplace( std::initializer_list il, A&&... a ) { variant_base::template emplace( il, std::forward(a)... ); return _get_impl( mp_size_t() ); @@ -1044,7 +1067,7 @@ public: private: - template> static variant _subset_impl( mp_size_t, V && v ) + template> static constexpr variant _subset_impl( mp_size_t, V && v ) { return variant( in_place_index, std::forward(v) ); } diff --git a/test/Jamfile b/test/Jamfile index c11eef5..b9ad29b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -33,7 +33,9 @@ run variant_copy_assign.cpp : : : $(REQ) ; run variant_move_assign.cpp : : : $(REQ) ; run variant_value_assign.cpp : : : $(REQ) ; run variant_emplace_index.cpp : : : $(REQ) ; +compile variant_emplace_index_cx.cpp : : : $(REQ) ; run variant_emplace_type.cpp : : : $(REQ) ; +compile variant_emplace_type_cx.cpp : : : $(REQ) ; run variant_swap.cpp : : : $(REQ) ; run variant_eq_ne.cpp : : : $(REQ) ; run variant_destroy.cpp : : : $(REQ) ; diff --git a/test/variant_emplace_index_cx.cpp b/test/variant_emplace_index_cx.cpp new file mode 100644 index 0000000..3a5d73d --- /dev/null +++ b/test/variant_emplace_index_cx.cpp @@ -0,0 +1,83 @@ + +// 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 + +using namespace boost::variant2; + +struct X +{ + int v; + constexpr X(): v( 0 ) {} + constexpr explicit X( int v ): v( v ) {} + constexpr operator int() const { return v; } +}; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template constexpr A test( A const& a ) +{ + V v; + + v.template emplace( a ); + + return get(v); +} + +int main() +{ + { + constexpr auto w = test, 0>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, 0>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, 0>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, 1>( 3.0f ); + STATIC_ASSERT( w == 3.0f ); + } + + { + constexpr auto w = test, 0>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, 1>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, 2>( 2.0f ); + STATIC_ASSERT( w == 2.0f ); + } + + { + constexpr auto w = test, 3>( 3.0f ); + STATIC_ASSERT( w == 3.0f ); + } + + { + constexpr auto w = test, 4>( 4 ); + STATIC_ASSERT( w == 4 ); + } + + { + constexpr auto w = test, 5>( 5 ); + STATIC_ASSERT( w == 5 ); + } +} diff --git a/test/variant_emplace_type_cx.cpp b/test/variant_emplace_type_cx.cpp new file mode 100644 index 0000000..4162484 --- /dev/null +++ b/test/variant_emplace_type_cx.cpp @@ -0,0 +1,63 @@ + +// 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 + +using namespace boost::variant2; + +struct X +{ + int v; + constexpr X(): v( 0 ) {} + constexpr explicit X( int v ): v( v ) {} + constexpr operator int() const { return v; } +}; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template constexpr A test( A const& a ) +{ + V v; + + v.template emplace( a ); + + return get(v); +} + +int main() +{ + { + constexpr auto w = test, int>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, X>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, int>( 1 ); + STATIC_ASSERT( w == 1 ); + } + + { + constexpr auto w = test, float>( 3.0f ); + STATIC_ASSERT( w == 3.0f ); + } + + { + constexpr auto w = test, float>( 3.0f ); + STATIC_ASSERT( w == 3.0f ); + } + + { + constexpr auto w = test, X>( 1 ); + STATIC_ASSERT( w == 1 ); + } +}