diff --git a/src/common/variant.h b/src/common/variant.h index ffb34e40a..8c68e3715 100644 --- a/src/common/variant.h +++ b/src/common/variant.h @@ -74,15 +74,14 @@ struct variant_static_visitor : public boost::static_visitor }; template -class variant final +class variant { - using VType = boost::variant; + using VType = boost::variant; public: //constructors /// default constructor variant() = default; - variant(boost::none_t) : variant{} {} //act like boost::optional /// construct from variant type (use enable_if to avoid issues with copy/move constructor) template ::type = true> variant(T &&value) : m_value{std::forward(value)} {} -//overloaded operators - /// boolean operator: true if the variant isn't empty/uninitialized - explicit operator bool() const noexcept { return !this->is_empty(); } - //member functions - /// check if empty/uninitialized - bool is_empty() const noexcept { return m_value.which() == 0; } - /// check the variant type template bool is_type() const noexcept { return this->index() == this->type_index_of(); } @@ -136,7 +128,7 @@ public: template static constexpr int type_index_of() noexcept { - using types = boost::mpl::vector; + using types = typename VType::types; using elem = typename boost::mpl::find::type; using begin = typename boost::mpl::begin::type; return boost::mpl::distance::value; @@ -162,6 +154,41 @@ private: //member variables /// variant of all value types VType m_value; + +//friend functions +template +friend bool do_serialize(Archive &ar, variant &v); +}; + +template +class optional_variant: public variant +{ +public: +//constructors + /// default constructor + optional_variant() = default; + + /// construct from variant type (use enable_if to avoid issues with copy/move constructor) + template >, + optional_variant + >::value, + bool + >::type = true> + optional_variant(T &&value) : variant(std::forward(value)) {} + + // construct like boost::optional + optional_variant(boost::none_t) {} + +//overloaded operators + /// boolean operator: true if the variant isn't empty/uninitialized + explicit operator bool() const noexcept { return !this->is_empty(); } + +//member functions + /// check if empty/uninitialized + bool is_empty() const noexcept { return this->index() == 0; } }; } //namespace tools diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 1cf93c8cf..a2d90a680 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -43,6 +43,7 @@ #include #include #include +#include "common/variant.h" #include "serialization.h" /*! \struct variant_serialization_triats @@ -144,3 +145,13 @@ static bool do_serialize(Archive &ar, boost::variant &v) { return boost::apply_visitor(variant_write_visitor(ar), v); } + +// implementation for tools::variant delegates to internal boost::variant member field +namespace tools +{ +template +bool do_serialize(Archive &ar, variant &v) +{ + return do_serialize(ar, v.m_value); +} +} diff --git a/tests/unit_tests/variant.cpp b/tests/unit_tests/variant.cpp index d7ded8e4b..dad99d824 100644 --- a/tests/unit_tests/variant.cpp +++ b/tests/unit_tests/variant.cpp @@ -38,6 +38,7 @@ #include #include +using tools::optional_variant; using tools::variant; using tools::variant_static_visitor; @@ -239,7 +240,7 @@ struct test_stringify_visitor: public variant_static_visitor //------------------------------------------------------------------------------------------------------------------- TEST(variant, operatorbool) { - variant v; + optional_variant v; EXPECT_FALSE(v); v = (int16_t) 2023; EXPECT_TRUE(v); @@ -251,7 +252,7 @@ TEST(variant, operatorbool) //------------------------------------------------------------------------------------------------------------------- TEST(variant, is_empty) { - variant v; + optional_variant v; EXPECT_TRUE(v.is_empty()); v = (int16_t) 2023; EXPECT_FALSE(v.is_empty()); @@ -260,7 +261,7 @@ TEST(variant, is_empty) v = boost::blank{}; EXPECT_TRUE(v.is_empty()); - variant<> v2; + optional_variant<> v2; EXPECT_TRUE(v2.is_empty()); v2 = boost::blank{}; EXPECT_TRUE(v2.is_empty()); @@ -269,7 +270,7 @@ TEST(variant, is_empty) TEST(variant, is_type) { variant v; - EXPECT_TRUE(v.is_type()); + EXPECT_TRUE(v.is_type()); v = (int16_t) 2023; EXPECT_TRUE(v.is_type()); @@ -279,7 +280,7 @@ TEST(variant, is_type) TEST(variant, try_unwrap) { variant v; - EXPECT_FALSE(v.try_unwrap()); + EXPECT_TRUE(v.try_unwrap()); v = (int16_t) 5252; ASSERT_TRUE(v.try_unwrap()); EXPECT_EQ(5252, *v.try_unwrap()); @@ -290,7 +291,7 @@ TEST(variant, try_unwrap) TEST(variant, unwrap) { variant v; - EXPECT_THROW(v.unwrap(), std::runtime_error); + EXPECT_EQ(0, v.unwrap()); v = (int16_t) 5252; EXPECT_EQ(5252, v.unwrap()); EXPECT_THROW(v.unwrap(), std::runtime_error); @@ -321,35 +322,55 @@ TEST(variant, index) variant v; EXPECT_EQ(0, v.index()); v = (int8_t) 7; - EXPECT_EQ(1, v.index()); + EXPECT_EQ(0, v.index()); v = (uint8_t) 7; - EXPECT_EQ(2, v.index()); + EXPECT_EQ(1, v.index()); v = (int16_t) 7; - EXPECT_EQ(3, v.index()); + EXPECT_EQ(2, v.index()); v = (uint16_t) 7; - EXPECT_EQ(4, v.index()); + EXPECT_EQ(3, v.index()); v = "verifiable variant vying for vengence versus visa"; - EXPECT_EQ(5, v.index()); + EXPECT_EQ(4, v.index()); + + optional_variant vo; + EXPECT_EQ(0, vo.index()); + vo = (int8_t) 7; + EXPECT_EQ(1, vo.index()); + vo = (uint8_t) 7; + EXPECT_EQ(2, vo.index()); + vo = (int16_t) 7; + EXPECT_EQ(3, vo.index()); + vo = (uint16_t) 7; + EXPECT_EQ(4, vo.index()); + vo = "verifiable variant vying for vengence versus visa"; + EXPECT_EQ(5, vo.index()); } //------------------------------------------------------------------------------------------------------------------- TEST(variant, type_index_of) { variant v; - EXPECT_EQ(0, decltype(v)::type_index_of()); - EXPECT_EQ(1, decltype(v)::type_index_of()); - EXPECT_EQ(2, decltype(v)::type_index_of()); - EXPECT_EQ(3, decltype(v)::type_index_of()); - EXPECT_EQ(4, decltype(v)::type_index_of()); - EXPECT_EQ(5, decltype(v)::type_index_of()); + EXPECT_EQ(0, decltype(v)::type_index_of()); + EXPECT_EQ(1, decltype(v)::type_index_of()); + EXPECT_EQ(2, decltype(v)::type_index_of()); + EXPECT_EQ(3, decltype(v)::type_index_of()); + EXPECT_EQ(4, decltype(v)::type_index_of()); + + optional_variant vo; + EXPECT_EQ(0, decltype(vo)::type_index_of()); + EXPECT_EQ(1, decltype(vo)::type_index_of()); + EXPECT_EQ(2, decltype(vo)::type_index_of()); + EXPECT_EQ(3, decltype(vo)::type_index_of()); + EXPECT_EQ(4, decltype(vo)::type_index_of()); + EXPECT_EQ(5, decltype(vo)::type_index_of()); } //------------------------------------------------------------------------------------------------------------------- TEST(variant, constexpr_type_index_of) { variant v; - constexpr int TINDEX0 = decltype(v)::type_index_of(); - EXPECT_EQ(0, TINDEX0); - constexpr int TINDEX5 = decltype(v)::type_index_of(); - EXPECT_EQ(5, TINDEX5); + constexpr int TINDEX2 = decltype(v)::type_index_of(); + EXPECT_EQ(2, TINDEX2); + constexpr int TINDEX4 = decltype(v)::type_index_of(); + EXPECT_EQ(4, TINDEX4); } //------------------------------------------------------------------------------------------------------------------- TEST(variant, same_type) @@ -362,7 +383,6 @@ TEST(variant, same_type) TEST(variant, visit) { variant v; - EXPECT_THROW(v.visit(test_stringify_visitor()), std::runtime_error); v = "Rev"; test_stringify_visitor::test_visitation(v, std::string("Rev")); @@ -377,7 +397,7 @@ TEST(variant, ad_hoc_recursion) struct left_t; struct right_t; - using twisty = variant, boost::recursive_wrapper>; + using twisty = optional_variant, boost::recursive_wrapper>; struct left_t {