Created
March 30, 2015 21:40
-
-
Save Redchards/594cfe5ab68f56293771 to your computer and use it in GitHub Desktop.
Simple variant type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef VARIANT | |
#define VARIANT | |
// Quick implementation of variant type, somewhat like boost::variant. | |
// Why not using boost then ? Boost is a big dependency, using it on such a tiny code | |
// would not be wise. | |
// NOTE : Is uint64 wise here ? I just chose it for security purpose | |
// Maybe using constexpr here would be wise. I don't want it to sound like a template metaprogramming showoff | |
#include <cstddef> | |
#include <type_traits> | |
#include <memory> | |
#include <utility> | |
#include <string> | |
#include <cassert> | |
#include <iostream> | |
template<class T, T holdValue> | |
struct ValueHolder | |
{ | |
static constexpr T value = holdValue; | |
}; | |
template<class T, class... Args> | |
struct MaxSizeof | |
{ | |
static constexpr size_t result = std::conditional<(sizeof(T) > MaxSizeof<Args...>::result), std::integral_constant<size_t, sizeof(T)>, std::integral_constant<size_t, MaxSizeof<Args...>::result>>::type::value; | |
}; | |
template<class T> | |
struct MaxSizeof<T> | |
{ | |
static constexpr size_t result = sizeof(T); | |
}; | |
template<class T, class... Args> | |
struct MaxAlignof | |
{ | |
static constexpr size_t result = std::conditional<(alignof(T) > MaxSizeof<Args...>::result), std::integral_constant<size_t, alignof(T)>, std::integral_constant<size_t, MaxSizeof<Args...>::result>>::type::value; | |
}; | |
template<class T> | |
struct MaxAlignof<T> | |
{ | |
static constexpr size_t result = alignof(T); | |
}; | |
template<class Head, class... Tail> | |
struct AnyOf | |
{ | |
static constexpr bool result = (Head::value || AnyOf<Tail...>::result); | |
}; | |
template<class Last> | |
struct AnyOf<Last> | |
{ | |
static constexpr bool result = Last::value; | |
}; | |
template<class T, class... Args> | |
struct AllOf | |
{ | |
static constexpr bool result = (T::value && AllOf<Args...>::result); | |
}; | |
template<class T> | |
struct AllOf<T> | |
{ | |
static constexpr bool result = T::value; | |
}; | |
template<size_t index, class T, class Head, class... Tail> | |
struct IndexOfRec | |
{ | |
static constexpr size_t result = (std::is_same<T, Head>::value ? index : IndexOfRec<index + 1, T, Tail...>::result); | |
}; | |
template<size_t index, class T, class Last> | |
struct IndexOfRec<index, T, Last> | |
{ | |
static constexpr size_t result = (std::is_same<T, Last>::value ? index : 0); // Index start at one, so 0 never matches. But should find a better way ! | |
}; | |
template<class T, class... Args> | |
struct IndexOf // Badly designed, should put index argument on last, or use terminal recursivity | |
{ | |
static constexpr size_t result = IndexOfRec<1, T, Args...>::result; | |
}; | |
struct NullType{}; | |
template<size_t index, class Head, class... Tail> | |
struct TypeAtIndex | |
{ | |
static_assert(index >= sizeof...(Tail), "Invalid index argument for this template"); | |
using type = typename std::conditional<index == 1, Head, typename TypeAtIndex<index - 1, Tail...>::type>::type; | |
}; | |
template<class... Args> | |
class Variant | |
{ | |
private: | |
//using StorageType = AlignedStorage<Args...>; | |
static_assert(AllOf<std::is_copy_constructible<typename std::remove_reference<Args>::type>...>::result && | |
AllOf<std::is_move_constructible<typename std::remove_reference<Args>::type>...>::result, | |
"All types need to be copy or move constructibles"); | |
static constexpr size_t align = MaxAlignof<Args...>::result; | |
static constexpr size_t size = MaxSizeof<Args...>::result; | |
typedef typename std::aligned_storage<size, align>::type StorageType; | |
private: | |
size_t current_; | |
StorageType storage_[size]; | |
public: | |
//TODO Implement default constructor | |
template<typename T, | |
typename std::enable_if< | |
std::is_same<typename std::decay<T>::type, Variant>::value>::type* = nullptr> | |
inline explicit Variant(T&& other) | |
: current_(std::forward<T>(other.current_)), | |
storage_(std::forward<T>(other.storage_)) // Useless ? decltype(other.current_) | |
{ | |
//*this = std::forward<T>(other); TODO : To fix | |
} | |
template<typename T, | |
typename std::enable_if< | |
AnyOf<std::is_same<typename std::remove_reference<T>::type, Args>...>::result && | |
!std::is_same<typename std::decay<T>::type, Variant>::value>::type* = nullptr> | |
explicit inline Variant(T&& rhs) : current_(IndexOf<typename std::remove_reference<T>::type, Args...>::result) | |
{ | |
new(storage_) typename std::remove_reference<T>::type(std::forward<T>(rhs)); | |
} | |
template<typename T, | |
typename std::enable_if< | |
AnyOf<std::is_same<typename std::remove_reference<T>::type, Args>...>::result >::type* = nullptr> | |
void assign(T&& rhs) | |
{ | |
constexpr size_t index = IndexOf<typename std::remove_reference<T>::type, Args...>::result; | |
if(index != current_) | |
{ | |
callDestructor(); | |
} | |
current_ = IndexOf<typename std::remove_reference<T>::type, Args...>::result; | |
*(static_cast<typename std::remove_reference<T>::type*>(static_cast<void*>(storage_))) = std::forward<T>(rhs); | |
} | |
template<typename T, | |
typename std::enable_if< | |
std::is_same<typename std::decay<T>::type, Variant>::value>::type* = nullptr> | |
T& operator=(T&& other) | |
{ | |
current_ = other.current_; | |
assign(other.storage_); | |
return this; | |
} | |
template<typename T, | |
typename std::enable_if< | |
AnyOf<std::is_same<T, Args>...>::result>::type* = nullptr> // Add checks | |
T get() | |
{ | |
assert((IndexOf<T, Args...>::result == current_)); // @TODO : Temporary | |
return *(static_cast<T*>(static_cast<void*>(storage_))); | |
} | |
// UNSAFE ! You must know what you are doing in order to use this function properly | |
// Provided to do conversion from pointer to int and back for the memory pool | |
// It's essentially an unsafe feature, but it's why I used union in the initial Memory Pool implementation | |
template<typename T, | |
typename std::enable_if< | |
AnyOf<std::is_same<T, Args>...>::result>::type* = nullptr> | |
void vary() | |
{ | |
current_ = IndexOf<T, Args...>::result; | |
} | |
private: | |
void callDestructor() | |
{ | |
callDestructorRec<Args...>(current_); | |
} | |
template<class Last> | |
void callDestructorRec(size_t index) | |
{ | |
if(index == 1) | |
{ | |
static_cast<Last*>(static_cast<void*>(storage_))->~Last(); | |
} | |
} | |
template<class Head1, class Head2, class... Tail> | |
void callDestructorRec(size_t index) | |
{ | |
if(index == 0) | |
{ | |
static_cast<Head1*>(static_cast<void*>(storage_))->~Head1(); | |
} | |
else | |
{ | |
callDestructorRec<Head2, Tail...>(index - 1); | |
} | |
} | |
}; | |
#endif // VARIANT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment