variant.hpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 id, void * data) { }
  63. inline static void move(size_t old_t, void * old_v, void * new_v) { }
  64. inline static void copy(size_t old_t, const void * old_v, void * new_v) { }
  65. };
  66. template <typename... Ts>
  67. struct variant {
  68. private:
  69. static const constexpr size_t data_size = static_max<sizeof(Ts)...>::value;
  70. static const constexpr size_t data_align = static_max<alignof(Ts)...>::value;
  71. using data_t = typename std::aligned_storage<data_size, data_align>::type;
  72. using helper_t = variant_helper<Ts...>;
  73. static inline size_t invalid_type() {
  74. return 0xFFFFFFFF;
  75. }
  76. size_t type_id;
  77. data_t data;
  78. public:
  79. variant() : type_id(invalid_type()), data() { }
  80. variant(const variant<Ts...>& old) : type_id(old.type_id), data()
  81. {
  82. helper_t::copy(old.type_id, &old.data, &data);
  83. }
  84. variant(variant<Ts...>&& old) : type_id(old.type_id), data()
  85. {
  86. helper_t::move(old.type_id, &old.data, &data);
  87. }
  88. // Serves as both the move and the copy asignment operator.
  89. variant<Ts...>& operator= (variant<Ts...> old)
  90. {
  91. std::swap(type_id, old.type_id);
  92. std::swap(data, old.data);
  93. return *this;
  94. }
  95. template<typename T>
  96. inline bool is() const {
  97. return (type_id == type_index<T, Ts...>::value);
  98. }
  99. inline bool valid() const {
  100. return (type_id != invalid_type());
  101. }
  102. template<typename T, typename... Args>
  103. void set(Args&&... args)
  104. {
  105. // First we destroy the current contents
  106. helper_t::destroy(type_id, &data);
  107. new (&data) T(std::forward<Args>(args)...);
  108. type_id = type_index<T, Ts...>::value;
  109. }
  110. template<typename T>
  111. T& get()
  112. {
  113. // It is a dynamic_cast-like behaviour
  114. if (type_id == type_index<T, Ts...>::value)
  115. return *reinterpret_cast<T*>(&data);
  116. else
  117. throw std::bad_cast();
  118. }
  119. template<typename T>
  120. T const& get() const
  121. {
  122. // It is a dynamic_cast-like behaviour
  123. if (type_id == type_index<T, Ts...>::value)
  124. return *reinterpret_cast<T const*>(&data);
  125. else
  126. throw std::bad_cast();
  127. }
  128. ~variant() {
  129. helper_t::destroy(type_id, &data);
  130. }
  131. };
  132. #endif /* variant_h */