Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tools: update variant #9336

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions src/common/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,14 @@ struct variant_static_visitor : public boost::static_visitor<ResultT>
};

template <typename... Types>
class variant final
class variant
{
using VType = boost::variant<boost::blank, Types...>;
using VType = boost::variant<Types...>;

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 <typename T,
Expand All @@ -95,14 +94,7 @@ class variant final
>::type = true>
variant(T &&value) : m_value{std::forward<T>(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 <typename T>
bool is_type() const noexcept { return this->index() == this->type_index_of<T>(); }
Expand Down Expand Up @@ -136,7 +128,7 @@ class variant final
template <typename T>
static constexpr int type_index_of() noexcept
{
using types = boost::mpl::vector<boost::blank, Types...>;
using types = typename VType::types;
using elem = typename boost::mpl::find<types, T>::type;
using begin = typename boost::mpl::begin<types>::type;
return boost::mpl::distance<begin, elem>::value;
Expand All @@ -162,6 +154,41 @@ class variant final
//member variables
/// variant of all value types
VType m_value;

//friend functions
template <class Archive, typename... Ts>
friend bool do_serialize(Archive &ar, variant<Ts...> &v);
};

template <typename... Types>
class optional_variant: public variant<boost::blank, Types...>
{
public:
//constructors
/// default constructor
optional_variant() = default;

/// construct from variant type (use enable_if to avoid issues with copy/move constructor)
template <typename T,
typename std::enable_if<
!std::is_same<
std::remove_cv_t<std::remove_reference_t<T>>,
optional_variant<Types...>
>::value,
bool
>::type = true>
optional_variant(T &&value) : variant<boost::blank, Types...>(std::forward<T>(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
11 changes: 11 additions & 0 deletions src/serialization/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <boost/mpl/if.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/pop_front.hpp>
#include "common/variant.h"
#include "serialization.h"

/*! \struct variant_serialization_triats
Expand Down Expand Up @@ -144,3 +145,13 @@ static bool do_serialize(Archive<true> &ar, boost::variant<T...> &v)
{
return boost::apply_visitor(variant_write_visitor<Archive>(ar), v);
}

// implementation for tools::variant delegates to internal boost::variant member field
namespace tools
{
template <class Archive, typename... Ts>
bool do_serialize(Archive &ar, variant<Ts...> &v)
{
return do_serialize(ar, v.m_value);
}
}
66 changes: 43 additions & 23 deletions tests/unit_tests/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <type_traits>
#include <vector>

using tools::optional_variant;
using tools::variant;
using tools::variant_static_visitor;

Expand Down Expand Up @@ -239,7 +240,7 @@ struct test_stringify_visitor: public variant_static_visitor<std::string>
//-------------------------------------------------------------------------------------------------------------------
TEST(variant, operatorbool)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
optional_variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
EXPECT_FALSE(v);
v = (int16_t) 2023;
EXPECT_TRUE(v);
Expand All @@ -251,7 +252,7 @@ TEST(variant, operatorbool)
//-------------------------------------------------------------------------------------------------------------------
TEST(variant, is_empty)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
optional_variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
EXPECT_TRUE(v.is_empty());
v = (int16_t) 2023;
EXPECT_FALSE(v.is_empty());
Expand All @@ -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());
Expand All @@ -269,7 +270,7 @@ TEST(variant, is_empty)
TEST(variant, is_type)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
EXPECT_TRUE(v.is_type<boost::blank>());
EXPECT_TRUE(v.is_type<int8_t>());
v = (int16_t) 2023;
EXPECT_TRUE(v.is_type<int16_t>());

Expand All @@ -279,7 +280,7 @@ TEST(variant, is_type)
TEST(variant, try_unwrap)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
EXPECT_FALSE(v.try_unwrap<int8_t>());
EXPECT_TRUE(v.try_unwrap<int8_t>());
v = (int16_t) 5252;
ASSERT_TRUE(v.try_unwrap<int16_t>());
EXPECT_EQ(5252, *v.try_unwrap<int16_t>());
Expand All @@ -290,7 +291,7 @@ TEST(variant, try_unwrap)
TEST(variant, unwrap)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
EXPECT_THROW(v.unwrap<int8_t>(), std::runtime_error);
EXPECT_EQ(0, v.unwrap<int8_t>());
v = (int16_t) 5252;
EXPECT_EQ(5252, v.unwrap<int16_t>());
EXPECT_THROW(v.unwrap<uint16_t>(), std::runtime_error);
Expand Down Expand Up @@ -321,35 +322,55 @@ TEST(variant, index)
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> 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<int8_t, uint8_t, int16_t, uint16_t, std::string> 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<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
EXPECT_EQ(0, decltype(v)::type_index_of<boost::blank>());
EXPECT_EQ(1, decltype(v)::type_index_of<int8_t>());
EXPECT_EQ(2, decltype(v)::type_index_of<uint8_t>());
EXPECT_EQ(3, decltype(v)::type_index_of<int16_t>());
EXPECT_EQ(4, decltype(v)::type_index_of<uint16_t>());
EXPECT_EQ(5, decltype(v)::type_index_of<std::string>());
EXPECT_EQ(0, decltype(v)::type_index_of<int8_t>());
EXPECT_EQ(1, decltype(v)::type_index_of<uint8_t>());
EXPECT_EQ(2, decltype(v)::type_index_of<int16_t>());
EXPECT_EQ(3, decltype(v)::type_index_of<uint16_t>());
EXPECT_EQ(4, decltype(v)::type_index_of<std::string>());

optional_variant<int8_t, uint8_t, int16_t, uint16_t, std::string> vo;
EXPECT_EQ(0, decltype(vo)::type_index_of<boost::blank>());
EXPECT_EQ(1, decltype(vo)::type_index_of<int8_t>());
EXPECT_EQ(2, decltype(vo)::type_index_of<uint8_t>());
EXPECT_EQ(3, decltype(vo)::type_index_of<int16_t>());
EXPECT_EQ(4, decltype(vo)::type_index_of<uint16_t>());
EXPECT_EQ(5, decltype(vo)::type_index_of<std::string>());
}
//-------------------------------------------------------------------------------------------------------------------
TEST(variant, constexpr_type_index_of)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
constexpr int TINDEX0 = decltype(v)::type_index_of<boost::blank>();
EXPECT_EQ(0, TINDEX0);
constexpr int TINDEX5 = decltype(v)::type_index_of<std::string>();
EXPECT_EQ(5, TINDEX5);
constexpr int TINDEX2 = decltype(v)::type_index_of<int16_t>();
EXPECT_EQ(2, TINDEX2);
constexpr int TINDEX4 = decltype(v)::type_index_of<std::string>();
EXPECT_EQ(4, TINDEX4);
}
//-------------------------------------------------------------------------------------------------------------------
TEST(variant, same_type)
Expand All @@ -362,7 +383,6 @@ TEST(variant, same_type)
TEST(variant, visit)
{
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
EXPECT_THROW(v.visit(test_stringify_visitor()), std::runtime_error);

v = "Rev";
test_stringify_visitor::test_visitation(v, std::string("Rev"));
Expand All @@ -377,7 +397,7 @@ TEST(variant, ad_hoc_recursion)
struct left_t;
struct right_t;

using twisty = variant<boost::recursive_wrapper<left_t>, boost::recursive_wrapper<right_t>>;
using twisty = optional_variant<boost::recursive_wrapper<left_t>, boost::recursive_wrapper<right_t>>;

struct left_t
{
Expand Down
Loading