variant.hpp 5.0 KB

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