// // variant.hpp // variant // // Created by Sam Jaffe on 1/30/16. // Copyright © 2016 Sam Jaffe. All rights reserved. // #ifndef variant_h #define variant_h #pragma once #include #include #include // Max of a list of values template struct static_max; template struct static_max { static const constexpr size_t value = T; }; template struct static_max { static const constexpr size_t value = T1 > T2 ? static_max::value : static_max::value; }; // Type index in a list template struct type_index; template struct type_index {}; template struct type_index { static const constexpr size_t value = type_index::value; }; template struct type_index { static const constexpr size_t value = sizeof...(Ts); }; template struct variant_helper; template struct variant_helper { inline static void destroy(size_t id, void * data) { if (id == sizeof...(Ts)) reinterpret_cast(data)->~F(); else variant_helper::destroy(id, data); } inline static void move(size_t old_t, void * old_v, void * new_v) { if (old_t == sizeof...(Ts)) new (new_v) F(std::move(*reinterpret_cast(old_v))); else variant_helper::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 == sizeof...(Ts)) new (new_v) F(*reinterpret_cast(old_v)); else variant_helper::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 struct variant { private: static const constexpr size_t data_size = static_max::value; static const constexpr size_t data_align = static_max::value; using data_t = typename std::aligned_storage::type; using helper_t = variant_helper; static inline size_t invalid_type() { return 0xFFFFFFFF; } size_t type_id; data_t data; public: variant() : type_id(invalid_type()), data() { } variant(const variant& old) : type_id(old.type_id), data() { helper_t::copy(old.type_id, &old.data, &data); } variant(variant&& old) : type_id(old.type_id), data() { helper_t::move(old.type_id, &old.data, &data); } // Serves as both the move and the copy asignment operator. variant& operator= (variant old) { std::swap(type_id, old.type_id); std::swap(data, old.data); return *this; } template inline bool is() const { return (type_id == type_index::value); } inline bool valid() const { return (type_id != invalid_type()); } template void set(Args&&... args) { // First we destroy the current contents helper_t::destroy(type_id, &data); new (&data) T(std::forward(args)...); type_id = type_index::value; } template T& get() { // It is a dynamic_cast-like behaviour if (type_id == type_index::value) return *reinterpret_cast(&data); else throw std::bad_cast(); } template T const& get() const { // It is a dynamic_cast-like behaviour if (type_id == type_index::value) return *reinterpret_cast(&data); else throw std::bad_cast(); } ~variant() { helper_t::destroy(type_id, &data); } }; #endif /* variant_h */