diff --git a/alib/src/std/variant.hpp b/alib/src/std/variant.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f9c3d641b8a3ffab4072936824d070b6fd4ff455 --- /dev/null +++ b/alib/src/std/variant.hpp @@ -0,0 +1,127 @@ +/** + * https://gist.github.com/tibordp/6909880 + * Created on: Feb 28, 2014 + * Author: Tibor Djurica Potpara + * Modified: Jan Travnicek + */ + +#include <iostream> +#include <utility> +#include <typeinfo> +#include <type_traits> +#include <string> + +template <size_t arg1, size_t ... others> +struct static_max; + +template <size_t arg> +struct static_max<arg> +{ + static const size_t value = arg; +}; + +template <size_t arg1, size_t arg2, size_t ... others> +struct static_max<arg1, arg2, others...> +{ + static const size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : + static_max<arg2, others...>::value; +}; + +template<typename... Ts> +struct variant_helper; + +template<typename F, typename... Ts> +struct variant_helper<F, Ts...> { + inline static void destroy(size_t id, void * data) + { + if (id == typeid(F).hash_code()) + reinterpret_cast<F*>(data)->~F(); + else + variant_helper<Ts...>::destroy(id, data); + } + + inline static void move(size_t old_t, void * old_v, void * new_v) + { + if (old_t == typeid(F).hash_code()) + new (new_v) F(std::move(*reinterpret_cast<F*>(old_v))); + else + variant_helper<Ts...>::move(old_t, old_v, new_v); + } + + inline static void copy(size_t old_t, const void * old_v, void * new_v) + { + if (old_t == typeid(F).hash_code()) + new (new_v) F(*reinterpret_cast<const F*>(old_v)); + else + variant_helper<Ts...>::copy(old_t, old_v, new_v); + } +}; + +template<> struct variant_helper<> { +inline static void destroy(size_t id, void * data) { } +inline static void move(size_t old_t, void * old_v, void * new_v) { } +inline static void copy(size_t old_t, const void * old_v, void * new_v) { } +}; + +template<typename F, typename... Ts> +struct variant { +private: + static const size_t data_size = static_max<sizeof(F), sizeof(Ts)...>::value; + static const size_t data_align = static_max<alignof(F), alignof(Ts)...>::value; + + using data_t = typename std::aligned_storage<data_size, data_align>::type; + + using helper_t = variant_helper<F, Ts...>; + + size_t type_id; + data_t data; +public: + variant() : type_id(typeid(F).hash_code()) { new (&data) F(); } + + variant(const variant<F, Ts...>& old) : type_id(old.type_id) + { + helper_t::copy(old.type_id, &old.data, &data); + } + + variant(variant<F, Ts...>&& old) : type_id(old.type_id) + { + helper_t::move(old.type_id, &old.data, &data); + } + + // Serves as both the move and the copy asignment operator. + variant<F, Ts...>& operator= (variant<F, Ts...> old) + { + std::swap(type_id, old.type_id); + std::swap(data, old.data); + + return *this; + } + + template<typename T> + void is() { + return (type_id == typeid(T).hash_code()); + } + + template<typename T, typename... Args> + void set(Args&&... args) + { + // First we destroy the current contents + helper_t::destroy(type_id, &data); + new (&data) T(std::forward<Args>(args)...); + type_id = typeid(T).hash_code(); + } + + template<typename T> + T& get() + { + // It is a dynamic_cast-like behaviour + if (type_id == typeid(T).hash_code()) + return *reinterpret_cast<T*>(&data); + else + throw std::bad_cast(); + } + + ~variant() { + helper_t::destroy(type_id, &data); + } +};