variant.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #pragma once
  2. namespace nall {
  3. template<typename T, typename... P> struct variant_size {
  4. static constexpr uint size = max(sizeof(T), variant_size<P...>::size);
  5. };
  6. template<typename T> struct variant_size<T> {
  7. static constexpr uint size = sizeof(T);
  8. };
  9. template<uint Index, typename F, typename T, typename... P> struct variant_index {
  10. static constexpr uint index = is_same_v<F, T> ? Index : variant_index<Index + 1, F, P...>::index;
  11. };
  12. template<uint Index, typename F, typename T> struct variant_index<Index, F, T> {
  13. static constexpr uint index = is_same_v<F, T> ? Index : 0;
  14. };
  15. template<typename T, typename... P> struct variant_copy {
  16. constexpr variant_copy(uint index, uint assigned, void* target, void* source) {
  17. if(index == assigned) new(target) T(*((T*)source));
  18. else variant_copy<P...>(index + 1, assigned, target, source);
  19. }
  20. };
  21. template<typename T> struct variant_copy<T> {
  22. constexpr variant_copy(uint index, uint assigned, void* target, void* source) {
  23. if(index == assigned) new(target) T(*((T*)source));
  24. }
  25. };
  26. template<typename T, typename... P> struct variant_move {
  27. constexpr variant_move(uint index, uint assigned, void* target, void* source) {
  28. if(index == assigned) new(target) T(move(*((T*)source)));
  29. else variant_move<P...>(index + 1, assigned, target, source);
  30. }
  31. };
  32. template<typename T> struct variant_move<T> {
  33. constexpr variant_move(uint index, uint assigned, void* target, void* source) {
  34. if(index == assigned) new(target) T(move(*((T*)source)));
  35. }
  36. };
  37. template<typename T, typename... P> struct variant_destruct {
  38. constexpr variant_destruct(uint index, uint assigned, void* data) {
  39. if(index == assigned) ((T*)data)->~T();
  40. else variant_destruct<P...>(index + 1, assigned, data);
  41. }
  42. };
  43. template<typename T> struct variant_destruct<T> {
  44. constexpr variant_destruct(uint index, uint assigned, void* data) {
  45. if(index == assigned) ((T*)data)->~T();
  46. }
  47. };
  48. template<typename F, typename T, typename... P> struct variant_equals {
  49. constexpr auto operator()(uint index, uint assigned) const -> bool {
  50. if(index == assigned) return is_same_v<F, T>;
  51. return variant_equals<F, P...>()(index + 1, assigned);
  52. }
  53. };
  54. template<typename F, typename T> struct variant_equals<F, T> {
  55. constexpr auto operator()(uint index, uint assigned) const -> bool {
  56. if(index == assigned) return is_same_v<F, T>;
  57. return false;
  58. }
  59. };
  60. template<typename... P> struct variant final { //final as destructor is not virtual
  61. variant() : assigned(0) {}
  62. variant(const variant& source) { operator=(source); }
  63. variant(variant&& source) { operator=(move(source)); }
  64. template<typename T> variant(const T& value) { operator=(value); }
  65. template<typename T> variant(T&& value) { operator=(move(value)); }
  66. ~variant() { reset(); }
  67. explicit operator bool() const { return assigned; }
  68. template<typename T> explicit constexpr operator T&() { return get<T>(); }
  69. template<typename T> explicit constexpr operator const T&() const { return get<T>(); }
  70. template<typename T> constexpr auto is() const -> bool {
  71. return variant_equals<T, P...>()(1, assigned);
  72. }
  73. template<typename T> constexpr auto get() -> T& {
  74. static_assert(variant_index<1, T, P...>::index, "type not in variant");
  75. struct variant_bad_cast{};
  76. if(!is<T>()) throw variant_bad_cast{};
  77. return *((T*)data);
  78. }
  79. template<typename T> constexpr auto get() const -> const T& {
  80. static_assert(variant_index<1, T, P...>::index, "type not in variant");
  81. struct variant_bad_cast{};
  82. if(!is<T>()) throw variant_bad_cast{};
  83. return *((const T*)data);
  84. }
  85. template<typename T> constexpr auto get(const T& fallback) const -> const T& {
  86. if(!is<T>()) return fallback;
  87. return *((const T*)data);
  88. }
  89. auto reset() -> void {
  90. if(assigned) variant_destruct<P...>(1, assigned, (void*)data);
  91. assigned = 0;
  92. }
  93. auto& operator=(const variant& source) {
  94. reset();
  95. if(assigned = source.assigned) variant_copy<P...>(1, source.assigned, (void*)data, (void*)source.data);
  96. return *this;
  97. }
  98. auto& operator=(variant&& source) {
  99. reset();
  100. if(assigned = source.assigned) variant_move<P...>(1, source.assigned, (void*)data, (void*)source.data);
  101. source.assigned = 0;
  102. return *this;
  103. }
  104. template<typename T> auto& operator=(const T& value) {
  105. static_assert(variant_index<1, T, P...>::index, "type not in variant");
  106. reset();
  107. new((void*)&data) T(value);
  108. assigned = variant_index<1, T, P...>::index;
  109. return *this;
  110. }
  111. template<typename T> auto& operator=(T&& value) {
  112. static_assert(variant_index<1, T, P...>::index, "type not in variant");
  113. reset();
  114. new((void*)&data) T(move(value));
  115. assigned = variant_index<1, T, P...>::index;
  116. return *this;
  117. }
  118. private:
  119. alignas(P...) char data[variant_size<P...>::size];
  120. uint assigned;
  121. };
  122. }