variant.hpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. //
  2. // variant.hpp
  3. // variant
  4. //
  5. // Created by Sam Jaffe on 1/30/16.
  6. // Copyright © 2016 Sam Jaffe. All rights reserved.
  7. //
  8. #ifndef variant_h
  9. #define variant_h
  10. #pragma once
  11. #include <cstdlib>
  12. #include <utility>
  13. #include <memory>
  14. // Max of a list of values
  15. template <size_t T, size_t... Ts> struct static_max;
  16. template <size_t T>
  17. struct static_max<T> {
  18. static const constexpr size_t value = T;
  19. };
  20. template <size_t T1, size_t T2, size_t... Ts>
  21. struct static_max<T1, T2, Ts...> {
  22. static const constexpr size_t value = T1 > T2 ? static_max<T1, Ts...>::value : static_max<T2, Ts...>::value;
  23. };
  24. // Type index in a list
  25. template <typename F, typename... Ts> struct type_index;
  26. template <typename F>
  27. struct type_index<F> {};
  28. template <typename F, typename T, typename... Ts>
  29. struct type_index<F, T, Ts...> {
  30. static const constexpr size_t value = type_index<F, Ts...>::value;
  31. };
  32. template <typename F, typename... Ts>
  33. struct type_index<F, F, Ts...> {
  34. static const constexpr size_t value = sizeof...(Ts);
  35. };
  36. template <typename... Ts> struct variant_helper;
  37. template<typename F, typename... Ts>
  38. struct variant_helper<F, Ts...> {
  39. inline static void destroy(size_t id, void * data)
  40. {
  41. if (id == sizeof...(Ts))
  42. reinterpret_cast<F*>(data)->~F();
  43. else
  44. variant_helper<Ts...>::destroy(id, data);
  45. }
  46. inline static void move(size_t old_t, void * old_v, void * new_v)
  47. {
  48. if (old_t == sizeof...(Ts))
  49. new (new_v) F(std::move(*reinterpret_cast<F*>(old_v)));
  50. else
  51. variant_helper<Ts...>::move(old_t, old_v, new_v);
  52. }
  53. inline static void copy(size_t old_t, const void * old_v, void * new_v)
  54. {
  55. if (old_t == sizeof...(Ts))
  56. new (new_v) F(*reinterpret_cast<const F*>(old_v));
  57. else
  58. variant_helper<Ts...>::copy(old_t, old_v, new_v);
  59. }
  60. };
  61. template<> struct variant_helper<> {
  62. inline static void destroy(size_t, void *) { }
  63. inline static void move(size_t, void *, void *) { }
  64. inline static void copy(size_t, const void *, void *) { }
  65. };
  66. template <typename... Ts>
  67. struct variant {
  68. private:
  69. static const constexpr size_t num_types = sizeof...(Ts);
  70. static const constexpr size_t data_size = static_max<sizeof(Ts)...>::value;
  71. static const constexpr size_t data_align = static_max<alignof(Ts)...>::value;
  72. using data_t = typename std::aligned_storage<data_size, data_align>::type;
  73. using helper_t = variant_helper<Ts...>;
  74. static inline size_t invalid_type() {
  75. return 0xFFFFFFFF;
  76. }
  77. template <typename T>
  78. static inline size_t get_type_index() {
  79. return num_types - type_index<T, Ts...>::value - 1;
  80. }
  81. size_t type_id;
  82. data_t data;
  83. public:
  84. template <size_t I>
  85. using at = typename std::tuple_element<I, std::tuple<Ts...>>::type;
  86. variant() : type_id(invalid_type()), data() { }
  87. template <typename T>
  88. variant(T const & value) : type_id(invalid_type()), data() {
  89. set<T>(value);
  90. }
  91. variant(const variant<Ts...>& old) : type_id(old.type_id), data() {
  92. helper_t::copy(num_types - type_id - 1, &old.data, &data);
  93. }
  94. variant(variant&& old) : type_id(old.type_id), data() {
  95. helper_t::move(num_types - type_id - 1, &old.data, &data);
  96. old.type_id = invalid_type();
  97. }
  98. variant& operator=(variant const & old) {
  99. helper_t::destroy(num_types - type_id - 1, &data);
  100. type_id = old.type_id;
  101. helper_t::copy(num_types - type_id - 1, &old.data, &data);
  102. return *this;
  103. }
  104. variant& operator=(variant&& old) {
  105. helper_t::destroy(num_types - type_id - 1, &data);
  106. type_id = old.type_id;
  107. helper_t::move(num_types - type_id - 1, &old.data, &data);
  108. old.type_id = invalid_type();
  109. return *this;
  110. }
  111. template<typename T>
  112. inline bool is() const {
  113. return (type_id == get_type_index<T>());
  114. }
  115. inline bool valid() const {
  116. return (type_id != invalid_type());
  117. }
  118. template<typename T, typename... Args>
  119. void set(Args&&... args)
  120. {
  121. // First we destroy the current contents
  122. helper_t::destroy(num_types - type_id - 1, &data);
  123. new (&data) T(std::forward<Args>(args)...);
  124. type_id = get_type_index<T>();
  125. }
  126. template<typename T>
  127. T& get()
  128. {
  129. // It is a dynamic_cast-like behaviour
  130. if (is<T>())
  131. return *reinterpret_cast<T*>(&data);
  132. else
  133. throw std::bad_cast();
  134. }
  135. template<typename T>
  136. T const& get() const
  137. {
  138. // It is a dynamic_cast-like behaviour
  139. if (is<T>())
  140. return *reinterpret_cast<T const*>(&data);
  141. else
  142. throw std::bad_cast();
  143. }
  144. size_t index() const { return type_id; }
  145. template <size_t I>
  146. auto get() const -> at<I> const & {
  147. return get<at<I>>();
  148. }
  149. template <size_t I>
  150. auto get() -> at<I> & {
  151. return get<at<I>>();
  152. }
  153. ~variant() {
  154. helper_t::destroy(num_types - type_id - 1, &data);
  155. }
  156. };
  157. namespace std {
  158. template <size_t I, typename... Ts>
  159. auto get(variant<Ts...> const & var) -> typename variant<Ts...>::template at<I> const & {
  160. return var.template get<I>();
  161. }
  162. template <size_t I, typename... Ts>
  163. auto get(variant<Ts...> & var) -> typename variant<Ts...>::template at<I> & {
  164. return var.template get<I>();
  165. }
  166. }
  167. #endif /* variant_h */