From 3cba28f3082df3e5740434c1fd264704e12706fc Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 25 Jul 2017 11:20:01 -0700 Subject: [PATCH] variant fixes and tests --- CHANGELOG.md | 1 + .../boost/beast/core/detail/type_traits.hpp | 16 ++ include/boost/beast/core/detail/variant.hpp | 21 ++- test/core/CMakeLists.txt | 1 + test/core/Jamfile | 1 + test/core/detail/variant.cpp | 137 +++++++++++++++--- 6 files changed, 152 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1e78cb6..28eb69ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 87: * Update appveyor for Boost branch * Rename to BEAST_EXPECT +* variant fixes and tests -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/detail/type_traits.hpp b/include/boost/beast/core/detail/type_traits.hpp index c5cee278..ed03ce66 100644 --- a/include/boost/beast/core/detail/type_traits.hpp +++ b/include/boost/beast/core/detail/type_traits.hpp @@ -82,6 +82,22 @@ max_sizeof() max_sizeof() : max_sizeof(); } +template +std::size_t constexpr +max_alignof() +{ + return alignof(U); +} + +template +std::size_t constexpr +max_alignof() +{ + return + max_alignof() > max_alignof() ? + max_alignof() : max_alignof(); +} + template struct repeat_tuple_impl { diff --git a/include/boost/beast/core/detail/variant.hpp b/include/boost/beast/core/detail/variant.hpp index 94f21b55..cba6ba63 100644 --- a/include/boost/beast/core/detail/variant.hpp +++ b/include/boost/beast/core/detail/variant.hpp @@ -32,7 +32,9 @@ template class variant { typename std::aligned_storage< - max_sizeof()>::type buf_; + max_sizeof(), + max_alignof() + >::type buf_; unsigned char i_ = 0; template @@ -51,6 +53,14 @@ public: destroy(C<0>{}); } + // 0 = empty + unsigned char + index() const + { + return i_; + } + + // moved-from object becomes empty variant(variant&& other) { i_ = other.move(&buf_, C<0>{}); @@ -61,6 +71,7 @@ public: i_ = other.copy(&buf_, C<0>{}); } + // moved-from object becomes empty variant& operator=(variant&& other) { if(i_ != 0) @@ -83,7 +94,6 @@ public: { if(i_ != 0) destroy(C<0>{}); - i_ = 0; new(&buf_) type( std::forward(args)...); i_ = I; @@ -99,7 +109,7 @@ public: } template - type& + type const& get() const { BOOST_ASSERT(i_ == I); @@ -130,6 +140,7 @@ private: { using T = type; get().~T(); + i_ = 0; return; } destroy(C{}); @@ -153,7 +164,7 @@ private: i_ = 0; return I+1; } - move(C{}); + return move(dest, C{}); } unsigned char @@ -173,7 +184,7 @@ private: new(dest) T{t}; return I+1; } - copy(C{}); + return copy(dest, C{}); } }; diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 0cf454fd..7786db51 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -52,5 +52,6 @@ add_executable (core-tests base64.cpp empty_base_optimization.cpp sha1.cpp + detail/variant.cpp detail/varint.cpp ) diff --git a/test/core/Jamfile b/test/core/Jamfile index 2e28899a..a157cdc9 100644 --- a/test/core/Jamfile +++ b/test/core/Jamfile @@ -41,5 +41,6 @@ unit-test core-tests : base64.cpp empty_base_optimization.cpp sha1.cpp + detail/variant.cpp detail/varint.cpp ; diff --git a/test/core/detail/variant.cpp b/test/core/detail/variant.cpp index 84e64845..d16127c6 100644 --- a/test/core/detail/variant.cpp +++ b/test/core/detail/variant.cpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace boost { @@ -29,17 +30,44 @@ public: return n; } + int value = 0; bool move = false; bool copy = false; - bool move_assign = false; - bool copy_assign = false; - Q() { ++count(); } - ~Q() { --count(); } - Q(Q&&) { ++count(); move = true; } - Q(Q const&) { ++count(); copy = true; } - Q& operator=(Q&&) { move_assign = true; return *this; } - Q& operator=(Q const&) { copy_assign = true; return *this; } + Q& operator=(Q&& q) = delete; + Q& operator=(Q const& q) = delete; + + ~Q() + { + --count(); + } + + Q() + { + ++count(); + } + + Q(Q&& q) + { + if(q.value < 0) + throw std::exception{}; + ++count(); + move = true; + } + + Q(Q const& q) + { + if(q.value < 0) + throw std::exception{}; + ++count(); + copy = true; + } + + explicit Q(int value_) + : value(value_) + { + ++count(); + } }; void @@ -58,6 +86,9 @@ public: v.emplace<1>(10); BEAST_EXPECT(v.index() == 1); BEAST_EXPECT(v.get<1>() == 10); + v.emplace<1>(20); + BEAST_EXPECT(v.index() == 1); + BEAST_EXPECT(v.get<1>() == 20); } { variant v; @@ -70,6 +101,10 @@ public: BEAST_EXPECT(Q<1>::count() == 0); v.emplace<1>(); BEAST_EXPECT(Q<1>::count() == 1); + BEAST_EXPECT(v.get<1>().value == 0); + v.emplace<1>(42); + BEAST_EXPECT(Q<1>::count() == 1); + BEAST_EXPECT(v.get<1>().value == 42); v.reset(); BEAST_EXPECT(Q<1>::count() == 0); } @@ -200,40 +235,51 @@ public: BEAST_EXPECT(v2.index() == 0); BEAST_EXPECT(v3.get<1>() == "Hello"); } + { + variant, Q<2>> v1; + variant, Q<2>> v2; + v1.emplace<1>(); + BEAST_EXPECT(v1.index() == 1); + BEAST_EXPECT(Q<1>::count() == 1); + v2.emplace<2>(); + BEAST_EXPECT(v2.index() == 2); + BEAST_EXPECT(Q<2>::count() == 1); + v2 = std::move(v1); + BEAST_EXPECT(v1.index() == 0); + BEAST_EXPECT(v2.index() == 1); + BEAST_EXPECT(Q<1>::count() == 1); + BEAST_EXPECT(Q<2>::count() == 0); + } + BEAST_EXPECT(Q<1>::count() == 0); + BEAST_EXPECT(Q<2>::count() == 0); { variant> v1; v1.emplace<1>(); - BEAST_EXPECT(! v1.get<1>().move_assign); BEAST_EXPECT(Q<1>::count() == 1); variant> v2; v2.emplace<1>(); BEAST_EXPECT(Q<1>::count() == 2); - BEAST_EXPECT(! v2.get<1>().move_assign); v2 = std::move(v1); BEAST_EXPECT(v1.index() == 0); BEAST_EXPECT(v2.index() == 1); BEAST_EXPECT(v2.get<1>().move); - BEAST_EXPECT(!v2.get<1>().move_assign); BEAST_EXPECT(Q<1>::count() == 1); } BEAST_EXPECT(Q<1>::count() == 0); { variant, Q<2>, Q<3>> v1; v1.emplace<2>(); - BEAST_EXPECT(! v1.get<2>().move_assign); BEAST_EXPECT(Q<1>::count() == 0); BEAST_EXPECT(Q<2>::count() == 1); BEAST_EXPECT(Q<3>::count() == 0); variant, Q<2>, Q<3>> v2; v2.emplace<2>(); - BEAST_EXPECT(! v2.get<2>().move_assign); BEAST_EXPECT(Q<1>::count() == 0); BEAST_EXPECT(Q<2>::count() == 2); BEAST_EXPECT(Q<3>::count() == 0); v2 = std::move(v1); BEAST_EXPECT(v1.index() == 0); BEAST_EXPECT(v2.index() == 2); - BEAST_EXPECT(! v2.get<2>().move_assign); BEAST_EXPECT(v2.get<2>().move); BEAST_EXPECT(Q<1>::count() == 0); BEAST_EXPECT(Q<2>::count() == 1); @@ -257,19 +303,31 @@ public: BEAST_EXPECT(v2.get<1>() == "Hello"); BEAST_EXPECT(v3.get<1>() == "Hello"); } + { + variant, Q<2>> v1; + variant, Q<2>> v2; + v1.emplace<1>(); + BEAST_EXPECT(v1.index() == 1); + BEAST_EXPECT(Q<1>::count() == 1); + v2.emplace<2>(); + BEAST_EXPECT(v2.index() == 2); + BEAST_EXPECT(Q<2>::count() == 1); + v2 = v1; + BEAST_EXPECT(v1.index() == 1); + BEAST_EXPECT(v2.index() == 1); + BEAST_EXPECT(Q<1>::count() == 2); + BEAST_EXPECT(Q<2>::count() == 0); + } { variant> v1; v1.emplace<1>(); - BEAST_EXPECT(! v1.get<1>().copy_assign); BEAST_EXPECT(Q<1>::count() == 1); variant> v2; v2.emplace<1>(); BEAST_EXPECT(Q<1>::count() == 2); - BEAST_EXPECT(! v2.get<1>().copy_assign); v2 = v1; BEAST_EXPECT(v1.index() == 1); BEAST_EXPECT(v2.index() == 1); - BEAST_EXPECT(! v2.get<1>().copy_assign); BEAST_EXPECT(v2.get<1>().copy); BEAST_EXPECT(Q<1>::count() == 2); } @@ -277,20 +335,17 @@ public: { variant, Q<2>, Q<3>> v1; v1.emplace<2>(); - BEAST_EXPECT(! v1.get<2>().copy_assign); BEAST_EXPECT(Q<1>::count() == 0); BEAST_EXPECT(Q<2>::count() == 1); BEAST_EXPECT(Q<3>::count() == 0); variant, Q<2>, Q<3>> v2; v2.emplace<2>(); - BEAST_EXPECT(! v2.get<2>().copy_assign); BEAST_EXPECT(Q<1>::count() == 0); BEAST_EXPECT(Q<2>::count() == 2); BEAST_EXPECT(Q<3>::count() == 0); v2 = v1; BEAST_EXPECT(v1.index() == 2); BEAST_EXPECT(v2.index() == 2); - BEAST_EXPECT(! v2.get<2>().copy_assign); BEAST_EXPECT(v2.get<2>().copy); BEAST_EXPECT(Q<1>::count() == 0); BEAST_EXPECT(Q<2>::count() == 2); @@ -340,6 +395,48 @@ public: BEAST_EXPECT(Q<1>::count() == 0); BEAST_EXPECT(Q<2>::count() == 0); BEAST_EXPECT(Q<3>::count() == 0); + + // basic guarantee + { + // move + variant> v1; + v1.emplace<1>(); + BEAST_EXPECT(Q<1>::count() == 1); + variant> v2; + v2.emplace<1>(-1); + BEAST_EXPECT(Q<1>::count() == 2); + try + { + v1 = std::move(v2); + fail(); + } + catch(std::exception const&) + { + BEAST_EXPECT(v1.index() == 0); + BEAST_EXPECT(Q<1>::count() == 1); + } + } + BEAST_EXPECT(Q<1>::count() == 0); + { + // copy + variant> v1; + v1.emplace<1>(); + BEAST_EXPECT(Q<1>::count() == 1); + variant> v2; + v2.emplace<1>(-1); + BEAST_EXPECT(Q<1>::count() == 2); + try + { + v1 = v2; + fail(); + } + catch(std::exception const&) + { + BEAST_EXPECT(v1.index() == 0); + BEAST_EXPECT(Q<1>::count() == 1); + } + } + BEAST_EXPECT(Q<1>::count() == 0); } void