Skip to content

Commit

Permalink
split variant into plain and optional, add variant serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
UkoeHB committed Aug 2, 2024
1 parent 35eb5c1 commit 04fe6fa
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 34 deletions.
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

0 comments on commit 04fe6fa

Please sign in to comment.