Skip to content
Snippets Groups Projects
Commit 72a569b5 authored by Jan Trávníček's avatar Jan Trávníček
Browse files

support void in variant type

parent 4229fb1d
No related branches found
No related tags found
No related merge requests found
...@@ -17,14 +17,12 @@ template <size_t arg1, size_t ... others> ...@@ -17,14 +17,12 @@ template <size_t arg1, size_t ... others>
struct static_max; struct static_max;
   
template <size_t arg> template <size_t arg>
struct static_max<arg> struct static_max<arg> {
{
static const size_t value = arg; static const size_t value = arg;
}; };
   
template <size_t arg1, size_t arg2, size_t ... others> template <size_t arg1, size_t arg2, size_t ... others>
struct static_max<arg1, arg2, others...> struct static_max<arg1, arg2, others...> {
{
static const size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static const size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value :
static_max<arg2, others...>::value; static_max<arg2, others...>::value;
}; };
...@@ -34,24 +32,21 @@ struct variant_helper; ...@@ -34,24 +32,21 @@ struct variant_helper;
   
template<typename F, typename... Ts> template<typename F, typename... Ts>
struct variant_helper<F, Ts...> { struct variant_helper<F, Ts...> {
inline static void destroy(size_t id, void * data) inline static void destroy(size_t id, void * data) {
{
if (id == typeid(F).hash_code()) if (id == typeid(F).hash_code())
reinterpret_cast<F*>(data)->~F(); reinterpret_cast<F*>(data)->~F();
else else
variant_helper<Ts...>::destroy(id, data); variant_helper<Ts...>::destroy(id, data);
} }
   
inline static void copy(size_t old_t, const void * old_v, void * new_v) inline static void copy(size_t old_t, const void * old_v, void * new_v) {
{
if (old_t == typeid(F).hash_code()) if (old_t == typeid(F).hash_code())
new (new_v) F(*reinterpret_cast<const F*>(old_v)); new (new_v) F(*reinterpret_cast<const F*>(old_v));
else else
return variant_helper<Ts...>::copy(old_t, old_v, new_v); variant_helper<Ts...>::copy(old_t, old_v, new_v);
} }
   
inline static void move(size_t old_t, void * old_v, void * new_v) inline static void move(size_t old_t, void * old_v, void * new_v) {
{
if (old_t == typeid(F).hash_code()) if (old_t == typeid(F).hash_code())
new (new_v) F(std::move(*reinterpret_cast<F*>(old_v))); new (new_v) F(std::move(*reinterpret_cast<F*>(old_v)));
else else
...@@ -72,8 +67,7 @@ struct variant_helper<F, Ts...> { ...@@ -72,8 +67,7 @@ struct variant_helper<F, Ts...> {
return variant_helper<Ts...>::string(id, data); return variant_helper<Ts...>::string(id, data);
} }
   
inline static int compareHelper(size_t this_t, const void * this_v, size_t other_t, const void * other_v) inline static int compareHelper(size_t this_t, const void * this_v, size_t other_t, const void * other_v) {
{
if (this_t == typeid(F).hash_code() && other_t != typeid(F).hash_code()) return -1; if (this_t == typeid(F).hash_code() && other_t != typeid(F).hash_code()) return -1;
if (this_t != typeid(F).hash_code() && other_t == typeid(F).hash_code()) return 1; if (this_t != typeid(F).hash_code() && other_t == typeid(F).hash_code()) return 1;
   
...@@ -85,6 +79,48 @@ struct variant_helper<F, Ts...> { ...@@ -85,6 +79,48 @@ struct variant_helper<F, Ts...> {
} }
}; };
   
template<typename... Ts>
struct variant_helper<void, Ts...> {
inline static void destroy(size_t id, void * data) {
if (id != typeid(void).hash_code())
variant_helper<Ts...>::destroy(id, data);
}
inline static void copy(size_t old_t, const void * old_v, void * new_v) {
if (old_t != typeid(void).hash_code())
variant_helper<Ts...>::copy(old_t, old_v, new_v);
}
inline static void move(size_t old_t, void * old_v, void * new_v) {
if (old_t != typeid(void).hash_code())
variant_helper<Ts...>::move(old_t, old_v, new_v);
}
inline static void print(ostream& out, const size_t id, const void* data) {
if (id == typeid(void).hash_code())
out << "void";
else
variant_helper<Ts...>::print(out, id, data);
}
inline static std::string string(const size_t id, const void* data) {
if (id == typeid(void).hash_code())
return "void";
else
return variant_helper<Ts...>::string(id, data);
}
inline static int compareHelper(size_t this_t, const void * this_v, size_t other_t, const void * other_v) {
if (this_t == typeid(void).hash_code() && other_t != typeid(void).hash_code()) return -1;
if (this_t != typeid(void).hash_code() && other_t == typeid(void).hash_code()) return 1;
if (this_t == typeid(void).hash_code() && other_t == typeid(void).hash_code())
return 0;
else
return variant_helper<Ts...>::compareHelper(this_t, this_v, other_t, other_v);
}
};
template<> struct variant_helper<> { template<> struct variant_helper<> {
inline static void destroy(size_t, void *) { } inline static void destroy(size_t, void *) { }
inline static void copy(size_t, const void *, void *) { } inline static void copy(size_t, const void *, void *) { }
...@@ -103,121 +139,141 @@ class variant_base<ST, AT> { ...@@ -103,121 +139,141 @@ class variant_base<ST, AT> {
static const size_t data_align = AT::value; static const size_t data_align = AT::value;
   
using data_t = typename std::aligned_storage<data_size, data_align>::type; using data_t = typename std::aligned_storage<data_size, data_align>::type;
protected:
   
protected:
size_t type_id; size_t type_id;
data_t data; data_t data;
   
variant_base() { this->type_id = 0; } variant_base( size_t type_id ) : type_id ( type_id ) { }
}; };
   
template<typename ST, typename AT, typename F, typename... Ts> template<typename ST, typename AT, typename F, typename... Ts>
class variant_base<ST, AT, F, Ts...> : public variant_base<ST, AT, Ts...> { class variant_base<ST, AT, F, Ts...> : public variant_base<ST, AT, Ts...> {
using variant_base<ST, AT, Ts...>::variant_base;
protected: protected:
variant_base() { this->type_id = 0; } variant_base( size_t type_id ) : variant_base<ST, AT, Ts...>::variant_base ( type_id ) { }
public: public:
variant_base(const F& value) : variant_base<ST, AT, Ts...>::variant_base ( typeid(F).hash_code() ) { new (&this->data) F(value); }
variant_base(F&& value) : variant_base<ST, AT, Ts...>::variant_base ( typeid(F).hash_code() ) { new (&this->data) F(std::move(value)); }
};
template<typename ST, typename AT, typename... Ts>
class variant_base<ST, AT, void, Ts...> : public variant_base<ST, AT, Ts...> {
using variant_base<ST, AT, Ts...>::variant_base; using variant_base<ST, AT, Ts...>::variant_base;
   
variant_base(const F& value) { this->type_id = typeid(F).hash_code(); new (&this->data) F(value); } protected:
variant_base(F&& value) { this->type_id = typeid(F).hash_code(); new (&this->data) F(std::move(value)); } variant_base( size_t type_id ) : variant_base<ST, AT, Ts...>::variant_base ( type_id ) { }
public:
variant_base() : variant_base<ST, AT, Ts...>::variant_base ( typeid(void).hash_code() ) { }
};
template<class T>
struct SizeOf {
static const size_t size = sizeof(T);
};
template<>
struct SizeOf<void> {
static const size_t size = 0;
};
template<class T>
struct AlignOf {
static const size_t align = alignof(T);
};
template<>
struct AlignOf<void> {
static const size_t align = 1;
}; };
   
template<typename ... Ts> template<typename ... Ts>
class variant : public variant_base<static_max<sizeof(Ts)...>, static_max<alignof(Ts)...>, Ts...> { class variant : public variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> {
using helper_t = variant_helper<Ts...>; using helper_t = variant_helper<Ts...>;
   
public: public:
using variant_base<static_max<sizeof(Ts)...>, static_max<alignof(Ts)...>, Ts...>::variant_base; using variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...>::variant_base;
template < typename = std::enable_if < std::is_base_of_any<void, Ts...>::value > >
variant ( ) : variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> ( ) {
}
   
//copy consructor //copy consructor
variant(const variant<Ts...>& old) : variant_base<static_max<sizeof(Ts)...>, static_max<alignof(Ts)...>, Ts...>() variant(const variant<Ts...>& old) : variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> ( old.type_id ) {
{
this->type_id = old.type_id;
helper_t::copy(old.type_id, &old.data, &this->data); helper_t::copy(old.type_id, &old.data, &this->data);
} }
   
//move constructor //move constructor
variant(variant<Ts...>&& old) noexcept : variant_base<static_max<sizeof(Ts)...>, static_max<alignof(Ts)...>, Ts...>() variant(variant<Ts...>&& old) noexcept : variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> ( old.type_id ) {
{
this->type_id = old.type_id;
helper_t::move(old.type_id, &old.data, &this->data); helper_t::move(old.type_id, &old.data, &this->data);
} }
   
//assignment operator //assignment operator
variant<Ts...>& operator= (variant<Ts...> old) noexcept variant<Ts...>& operator= (variant<Ts...> old) noexcept {
{
std::swap(this->type_id, old.type_id); std::swap(this->type_id, old.type_id);
std::swap(this->data, old.data); std::swap(this->data, old.data);
   
return *this; return *this;
} }
   
bool operator== (const variant<Ts...>& other) const {
bool operator== (const variant<Ts...>& other) const
{
return helper_t::compareHelper(this->type_id, &this->data, other.type_id, &other.data) == 0; return helper_t::compareHelper(this->type_id, &this->data, other.type_id, &other.data) == 0;
} }
   
bool operator!= (const variant<Ts...>& other) const bool operator!= (const variant<Ts...>& other) const {
{
return !(*this == other); return !(*this == other);
} }
   
bool operator< (const variant<Ts...>& other) const bool operator< (const variant<Ts...>& other) const {
{
return helper_t::compareHelper(this->type_id, &this->data, other.type_id, &other.data) < 0; return helper_t::compareHelper(this->type_id, &this->data, other.type_id, &other.data) < 0;
} }
   
bool operator> (const variant<Ts...>& other) const bool operator> (const variant<Ts...>& other) const {
{
return other < *this; return other < *this;
} }
   
bool operator<= (const variant<Ts...>& other) const bool operator<= (const variant<Ts...>& other) const {
{
return !(*this > other); return !(*this > other);
} }
   
bool operator>= (const variant<Ts...>& other) const bool operator>= (const variant<Ts...>& other) const {
{
return !(*this < other); return !(*this < other);
} }
   
int compare(const variant<Ts...>& other) const int compare(const variant<Ts...>& other) const {
{
return helper_t::compareHelper(this->type_id, &this->data, other.type_id, &other.data); return helper_t::compareHelper(this->type_id, &this->data, other.type_id, &other.data);
} }
   
template<typename T> template<typename T, typename = std::enable_if < std::is_base_of_any<T, Ts...>::value > >
bool is() const { bool is() const {
return (this->type_id == typeid(T).hash_code()); return (this->type_id == typeid(T).hash_code());
} }
   
template<typename T> template<typename = std::enable_if < std::is_base_of_any<void, Ts...>::value > >
void set(T&& value) void set ( ) {
{ helper_t::destroy(this->type_id, &this->data);
if(std::is_base_of_any<T, Ts...>::value) { this->type_id = typeid(void).hash_code();
helper_t::destroy(this->type_id, &this->data);
new (&this->data) T(value);
this->type_id = typeid(T).hash_code();
} else
throw std::bad_cast();
} }
   
template<typename T> template<typename T, typename = std::enable_if < std::is_base_of_any<T, Ts...>::value && ! std::is_same < void, T >::value > >
void set(const T& value) void set(T&& value) {
{ helper_t::destroy(this->type_id, &this->data);
if(std::is_base_of_any<T, Ts...>::value) { new (&this->data) T(value);
helper_t::destroy(this->type_id, &this->data); this->type_id = typeid(T).hash_code();
new (&this->data) T(std::move(value));
this->type_id = typeid(T).hash_code();
} else
throw std::bad_cast();
} }
   
template<typename T> template<typename T, typename = std::enable_if < std::is_base_of_any<T, Ts...>::value && ! std::is_same < void, T >::value > >
T& get() void set(const T& value) {
{ helper_t::destroy(this->type_id, &this->data);
new (&this->data) T(std::move(value));
this->type_id = typeid(T).hash_code();
}
template<typename T, typename = std::enable_if < std::is_base_of_any<T, Ts...>::value && ! std::is_same < void, T >::value > >
T& get() {
// It is a dynamic_cast-like behaviour // It is a dynamic_cast-like behaviour
if (this->type_id == typeid(T).hash_code()) if (this->type_id == typeid(T).hash_code())
return *reinterpret_cast<T*>(&this->data); return *reinterpret_cast<T*>(&this->data);
...@@ -225,9 +281,8 @@ public: ...@@ -225,9 +281,8 @@ public:
throw std::bad_cast(); throw std::bad_cast();
} }
   
template<typename T> template<typename T, typename = std::enable_if < std::is_base_of_any<T, Ts...>::value && ! std::is_same < void, T >::value > >
const T& get() const const T& get() const {
{
// It is a dynamic_cast-like behaviour // It is a dynamic_cast-like behaviour
if (this->type_id == typeid(T).hash_code()) if (this->type_id == typeid(T).hash_code())
return *reinterpret_cast<const T*>(&this->data); return *reinterpret_cast<const T*>(&this->data);
...@@ -247,6 +302,16 @@ public: ...@@ -247,6 +302,16 @@ public:
explicit operator std::string() const { explicit operator std::string() const {
return helper_t::string(this->type_id, &this->data); return helper_t::string(this->type_id, &this->data);
} }
template < typename T >
static typename std::enable_if < std::is_base_of_any<T, Ts...>::value && ! std::is_same < void, T >::value, variant < Ts ... > >::type from ( ) {
return variant < Ts ... >( T ( ) );
}
template < typename T >
static typename std::enable_if < std::is_base_of_any<T, Ts...>::value && std::is_same < void, T >::value, variant < Ts ... > >::type from ( ) {
return variant < Ts ... >( );
}
}; };
   
template<typename ... Ts> template<typename ... Ts>
......
...@@ -108,3 +108,28 @@ void VariantTest::testVariantSet2() { ...@@ -108,3 +108,28 @@ void VariantTest::testVariantSet2() {
   
CPPUNIT_ASSERT( s.size() == u.size()); CPPUNIT_ASSERT( s.size() == u.size());
} }
void VariantTest::testVariantVoid() {
std::variant<void, int> i(1);
std::variant<void, int> v;
CPPUNIT_ASSERT ( v.is < void > ( ) );
CPPUNIT_ASSERT ( i.is < int > ( ) );
i.set();
CPPUNIT_ASSERT ( i.is < void > ( ) );
std::variant<void, int> copy = i;
CPPUNIT_ASSERT ( copy == v );
v = 10;
CPPUNIT_ASSERT ( v.get<int>() == 10 );
}
void VariantTest::testVariantDefault() {
std::variant<int, std::string> v = std::variant<int, std::string>::from<std::string>();
CPPUNIT_ASSERT ( v.get<std::string>() == "");
}
...@@ -10,6 +10,8 @@ class VariantTest : public CppUnit::TestFixture ...@@ -10,6 +10,8 @@ class VariantTest : public CppUnit::TestFixture
CPPUNIT_TEST( testVariant ); CPPUNIT_TEST( testVariant );
CPPUNIT_TEST( testVariantSet ); CPPUNIT_TEST( testVariantSet );
CPPUNIT_TEST( testVariantSet2 ); CPPUNIT_TEST( testVariantSet2 );
CPPUNIT_TEST( testVariantVoid );
CPPUNIT_TEST( testVariantDefault );
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
   
public: public:
...@@ -68,6 +70,8 @@ public: ...@@ -68,6 +70,8 @@ public:
void testVariant(); void testVariant();
void testVariantSet(); void testVariantSet();
void testVariantSet2(); void testVariantSet2();
void testVariantVoid();
void testVariantDefault();
}; };
   
namespace std { namespace std {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment