// // variant.hpp // variant // // Created by Sam Jaffe on 1/30/16. // Copyright © 2016 Sam Jaffe. All rights reserved. // #pragma once #include #include #include #include "detail/helper.hpp" #include "detail/types.hpp" namespace variant { template class variant { public: template using at = typename std::tuple_element>::type; private: static const constexpr size_t num_types = sizeof...(Ts); static const constexpr size_t data_size = detail::static_max::value; static const constexpr size_t data_align = detail::static_max::value; using data_t = typename std::aligned_storage::type; private: size_t type_id; data_t data; public: 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() { detail::helper::copy(num_types - type_id - 1, &old.data, &data); } variant(variant && old) : type_id(old.type_id), data() { detail::helper::move(num_types - type_id - 1, &old.data, &data); old.type_id = invalid_type(); } variant & operator=(variant const & old) { detail::helper::destroy(num_types - type_id - 1, &data); type_id = old.type_id; detail::helper::copy(num_types - type_id - 1, &old.data, &data); return *this; } variant & operator=(variant && old) { detail::helper::destroy(num_types - type_id - 1, &data); type_id = old.type_id; detail::helper::move(num_types - type_id - 1, &old.data, &data); old.type_id = invalid_type(); return *this; } ~variant() { detail::helper::destroy(num_types - type_id - 1, &data); } 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 detail::helper::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>(); } private: static inline size_t invalid_type() { return 0xFFFFFFFF; } template static inline size_t get_type_index() { return num_types - detail::type_index::value - 1; } }; } namespace std { template auto get(variant::variant const & var) -> typename variant::variant::template at const & { return var.template get(); } template auto get(variant::variant & var) -> typename variant::variant::template at & { return var.template get(); } }