Skip to content

Instantly share code, notes, and snippets.

@Redchards
Created March 30, 2015 21:40
Show Gist options
  • Save Redchards/594cfe5ab68f56293771 to your computer and use it in GitHub Desktop.
Save Redchards/594cfe5ab68f56293771 to your computer and use it in GitHub Desktop.
Simple variant type
#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