variant.hpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  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. #pragma once
  9. #include <cstdlib>
  10. #include <memory>
  11. #include <utility>
  12. #include "detail/helper.hpp"
  13. #include "detail/types.hpp"
  14. namespace variant {
  15. template <typename... Ts> class variant {
  16. public:
  17. template <size_t I>
  18. using at = typename std::tuple_element<I, std::tuple<Ts...>>::type;
  19. private:
  20. static const constexpr size_t num_types = sizeof...(Ts);
  21. static const constexpr size_t data_size =
  22. detail::static_max<sizeof(Ts)...>::value;
  23. static const constexpr size_t data_align =
  24. detail::static_max<alignof(Ts)...>::value;
  25. using data_t = typename std::aligned_storage<data_size, data_align>::type;
  26. private:
  27. size_t type_id;
  28. data_t data;
  29. public:
  30. variant() : type_id(invalid_type()), data() {}
  31. template <typename T>
  32. variant(T const & value) : type_id(invalid_type()), data() {
  33. set<T>(value);
  34. }
  35. variant(const variant<Ts...> & old) : type_id(old.type_id), data() {
  36. detail::helper<Ts...>::copy(num_types - type_id - 1, &old.data, &data);
  37. }
  38. variant(variant && old) : type_id(old.type_id), data() {
  39. detail::helper<Ts...>::move(num_types - type_id - 1, &old.data, &data);
  40. old.type_id = invalid_type();
  41. }
  42. variant & operator=(variant const & old) {
  43. detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
  44. type_id = old.type_id;
  45. detail::helper<Ts...>::copy(num_types - type_id - 1, &old.data, &data);
  46. return *this;
  47. }
  48. variant & operator=(variant && old) {
  49. detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
  50. type_id = old.type_id;
  51. detail::helper<Ts...>::move(num_types - type_id - 1, &old.data, &data);
  52. old.type_id = invalid_type();
  53. return *this;
  54. }
  55. ~variant() {
  56. detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
  57. }
  58. template <typename T> inline bool is() const {
  59. return (type_id == get_type_index<T>());
  60. }
  61. inline bool valid() const { return (type_id != invalid_type()); }
  62. template <typename T, typename... Args> void set(Args &&... args) {
  63. // First we destroy the current contents
  64. detail::helper<Ts...>::destroy(num_types - type_id - 1, &data);
  65. new (&data) T(std::forward<Args>(args)...);
  66. type_id = get_type_index<T>();
  67. }
  68. template <typename T> T & get() {
  69. // It is a dynamic_cast-like behaviour
  70. if (is<T>()) {
  71. return *reinterpret_cast<T *>(&data);
  72. } else {
  73. throw std::bad_cast();
  74. }
  75. }
  76. template <typename T> T const & get() const {
  77. // It is a dynamic_cast-like behaviour
  78. if (is<T>()) {
  79. return *reinterpret_cast<T const *>(&data);
  80. } else {
  81. throw std::bad_cast();
  82. }
  83. }
  84. size_t index() const { return type_id; }
  85. template <size_t I> auto get() const -> at<I> const & {
  86. return get<at<I>>();
  87. }
  88. template <size_t I> auto get() -> at<I> & { return get<at<I>>(); }
  89. private:
  90. static inline size_t invalid_type() { return 0xFFFFFFFF; }
  91. template <typename T> static inline size_t get_type_index() {
  92. return num_types - detail::type_index<T, Ts...>::value - 1;
  93. }
  94. };
  95. }
  96. namespace std {
  97. template <size_t I, typename... Ts>
  98. auto get(variant::variant<Ts...> const & var) ->
  99. typename variant::variant<Ts...>::template at<I> const & {
  100. return var.template get<I>();
  101. }
  102. template <size_t I, typename... Ts>
  103. auto get(variant::variant<Ts...> & var) ->
  104. typename variant::variant<Ts...>::template at<I> & {
  105. return var.template get<I>();
  106. }
  107. }