// // 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, void *) { } inline static void move(size_t, void *, void *) { } inline static void copy(size_t, const void *, void *) { } }; template struct variant { private: static const constexpr size_t num_types = sizeof...(Ts); 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; } template static inline size_t get_type_index() { return num_types - type_index::value - 1; } size_t type_id; data_t data; public: template using at = typename std::tuple_element>::type; variant() : type_id(invalid_type()), data() { } template variant(T const & value) : type_id(invalid_type()), data() { set(value); } variant(const variant& old) : type_id(old.type_id), data() { helper_t::copy(num_types - old.type_id - 1, &old.data, &data); } variant(variant&& old) : type_id(old.type_id), data() { helper_t::move(num_types - old.type_id - 1, &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 == get_type_index()); } inline bool valid() const { return (type_id != invalid_type()); } template void set(Args&&... args) { // First we destroy the current contents helper_t::destroy(num_types - type_id - 1, &data); new (&data) T(std::forward(args)...); type_id = get_type_index(); } template T& get() { // It is a dynamic_cast-like behaviour if (is()) return *reinterpret_cast(&data); else throw std::bad_cast(); } template T const& get() const { // It is a dynamic_cast-like behaviour if (is()) return *reinterpret_cast(&data); else throw std::bad_cast(); } size_t index() const { return type_id; } template auto get() const -> at const & { return get>(); } template auto get() -> at & { return get>(); } ~variant() { helper_t::destroy(num_types - type_id - 1, &data); } }; namespace std { template auto get(variant const & var) -> typename variant::template at const & { return var.template get(); } template auto get(variant & var) -> typename variant::template at & { return var.template get(); } } #endif /* variant_h */