From 4de9f80487974567b2cb304ce0fb4e045ba221b6 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Wed, 14 Jun 2017 01:04:36 +0300 Subject: [PATCH] Require valueless to be first, add test --- include/boost/variant2/variant.hpp | 24 +-- test/Jamfile | 1 + test/variant_valueless.cpp | 235 +++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+), 11 deletions(-) create mode 100644 test/variant_valueless.cpp diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index b20bdf8..cd59f0e 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -407,7 +407,7 @@ template union variant_storage_impl rest_.emplace( mp_size_t(), std::forward(a)... ); } - template constexpr void emplace_impl( mp_true, mp_size_t, A&&... a ) noexcept + template constexpr void emplace_impl( mp_true, mp_size_t, A&&... a ) { *this = variant_storage_impl( mp_size_t(), std::forward(a)... ); } @@ -459,8 +459,10 @@ template using resolve_overload_index = mp_find using can_be_valueless = std::is_same>, valueless>; + template struct variant_base_impl; // trivially destructible, single buffered -template using variant_base = variant_base_impl...>::value, mp_any...>, std::is_same...>::value, T...>; +template using variant_base = variant_base_impl...>::value, mp_any...>, can_be_valueless>::value, T...>; struct none {}; @@ -517,10 +519,10 @@ template struct variant_base_impl template void emplace_impl( mp_false, mp_false, A&&... a ) { - std::size_t const K = mp_find, valueless>::value; - - if( K < sizeof...(T) ) // have valueless + if( can_be_valueless::value ) // T0 == valueless { + std::size_t const K = 0; + try { st1_.emplace( mp_size_t(), std::forward(a)... ); @@ -550,7 +552,7 @@ template struct variant_base_impl std::size_t const J = I+1; using U = mp_at_c, I>; - this->emplace_impl( std::is_nothrow_constructible(), mp_all, variant2::detail::is_trivially_move_assignable...>(), std::forward(a)... ); + this->emplace_impl( std::is_nothrow_constructible(), mp_all, variant2::detail::is_trivially_move_assignable...>(), std::forward(a)... ); } }; @@ -670,8 +672,6 @@ template struct variant_base_impl { size_t const J = I+1; - std::size_t const K = mp_find, valueless>::value; - using U = mp_at_c, I>; if( std::is_nothrow_constructible::value ) @@ -681,8 +681,10 @@ template struct variant_base_impl st1_.emplace( mp_size_t(), std::forward(a)... ); ix_ = J; } - else if( K < sizeof...(T) ) // have valueless + else if( can_be_valueless::value ) // T0 == valueless { + std::size_t const K = 0; + _destroy(); try @@ -692,8 +694,8 @@ template struct variant_base_impl } catch( ... ) { - st1_.emplace( mp_size_t() ); - ix_ = K; + st1_.emplace( mp_size_t() ); + ix_ = K+1; throw; } diff --git a/test/Jamfile b/test/Jamfile index 39973c9..9d082bf 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -63,3 +63,4 @@ run variant_visit.cpp : : : $(REQ) ; run variant_lt_gt.cpp : : : $(REQ) ; run variant_convert_construct.cpp : : : $(REQ) ; run variant_subset.cpp : : : $(REQ) ; +run variant_valueless.cpp : : : $(REQ) ; diff --git a/test/variant_valueless.cpp b/test/variant_valueless.cpp new file mode 100644 index 0000000..77bb166 --- /dev/null +++ b/test/variant_valueless.cpp @@ -0,0 +1,235 @@ + +// 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; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +enum E1 +{ + e1 +}; + +enum E2 +{ + e2 +}; + +struct X1 +{ + X1() = default; + X1( E2 ) { throw std::runtime_error( "X1(E2)" ); } +}; + +STATIC_ASSERT( std::is_nothrow_copy_constructible::value ); +STATIC_ASSERT( std::is_nothrow_move_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_constructible::value ); +STATIC_ASSERT( std::is_trivially_destructible::value ); + +struct X2 +{ + X2() = default; + ~X2(); + X2( E2 ) { throw std::runtime_error( "X1(E2)" ); } +}; + +X2::~X2() {} + +STATIC_ASSERT( std::is_nothrow_copy_constructible::value ); +STATIC_ASSERT( std::is_nothrow_move_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_constructible::value ); +STATIC_ASSERT( !std::is_trivially_destructible::value ); + +struct X3 +{ + X3() = default; + X3( X3 const& ) {} + X3( X3&& ) {} +}; + +STATIC_ASSERT( !std::is_nothrow_copy_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_move_constructible::value ); +STATIC_ASSERT( std::is_trivially_destructible::value ); + +int main() +{ + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2;` failed to throw" ); + } + catch( std::exception const& ) + { + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + } + } + + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2;` failed to throw" ); + } + catch( std::exception const& ) + { + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + } + } + + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2;` failed to throw" ); + } + catch( std::exception const& ) + { + // all types are trivial, so we get a constexpr path + // and v.index() stays 1 + + BOOST_TEST( v.index() == 0 || v.index() == 1 ); + + if( v.index() == 1 ) + { + BOOST_TEST_EQ( get<1>(v), e1 ); + } + } + } + + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2` failed to throw" ); + } + catch( std::exception const& ) + { + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + } + } + + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2;` failed to throw" ); + } + catch( std::exception const& ) + { + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + } + } + + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2;` failed to throw" ); + } + catch( std::exception const& ) + { + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + } + } + + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2;` failed to throw" ); + } + catch( std::exception const& ) + { + BOOST_TEST_EQ( v.index(), 0 ); + } + } + + { + variant v; + + v = e1; + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + + try + { + v = e2; + BOOST_ERROR( "`v = e2;` failed to throw" ); + } + catch( std::exception const& ) + { + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), e1 ); + } + } + + return boost::report_errors(); +}