General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 regels
7.2 KiB

4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
  1. #pragma once
  2. #include "gp_config.hpp"
  3. #include <gp/algorithms/move.hpp>
  4. #include <gp/algorithms/tmp_manip.hpp>
  5. #include "gp/utils/allocators/dummy.hpp"
  6. #include "gp/exception.hpp"
  7. #include "gp/functional/function.hpp"
  8. #include <type_traits>
  9. #include <new>
  10. // TODO: Implement the allocating flavour of variant
  11. namespace gp{
  12. /**
  13. * @brief A form of variant that expect only classes whose size is strictly defined at compile time
  14. *
  15. * @tparam T The list of types accepted by the variant
  16. */
  17. template<typename ...T>
  18. class fixed_variant final {
  19. std::size_t index = std::numeric_limits<std::size_t>::max();
  20. alignas(gp::max(alignof(T)...)) char data[max_size<T...>()];
  21. gp::function<void(void*, void*)> cpytor = {[](void*, void*){}, nullopt};
  22. gp::function<void(void*, void*)> mvtor = {[](void*, void*){}, nullopt};
  23. gp::function<void(void*)> dtor = {[](void*){}, nullopt};
  24. static_assert(all_of_fixed_size<T...>::value, "not fixed");
  25. public:
  26. fixed_variant()
  27. : fixed_variant(typename first_of<T...>::type{})
  28. {}
  29. /**
  30. * @brief Construct a new fixed variant from an object that is legal in it
  31. *
  32. * @tparam U A type that belongs in the list of types the variant supports
  33. * @param value The copied value
  34. */
  35. template<typename U, std::enable_if_t<list_contains_class<gp::remove_cvref_t<U>,T...>::value,int> = 0>
  36. fixed_variant(U& value)
  37. : index{r_index_of<gp::remove_cvref_t<U>, T...>::value}
  38. {
  39. using actual = gp::remove_cvref_t<U>;
  40. dtor = gp::function<void(void*)>([](void* thing){
  41. ((actual*)thing)->~actual();
  42. }, nullopt);
  43. cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){
  44. new(dest) actual(*(actual*)src);
  45. }, nullopt);
  46. mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){
  47. new(dest) actual(gp::move(*(actual*)src));
  48. }, nullopt);
  49. cpytor((char*)&value, data);
  50. }
  51. static_assert(list_contains_class<int, int>::value, "list_contains_class doesn't work properly");
  52. static_assert(list_contains_class<int, char, int>::value, "list_contains_class doesn't work properly");
  53. static_assert(list_contains_class<int, int, char>::value, "list_contains_class doesn't work properly");
  54. static_assert(list_contains_class<int, char, int, char>::value, "list_contains_class doesn't work properly");
  55. static_assert(!list_contains_class<int, char, char>::value, "list_contains_class doesn't work properly");
  56. /**
  57. * @brief Construct a new fixed variant from an object that is legal in it by moving said object
  58. *
  59. * @tparam U A type that belongs in the list of types the variant supports
  60. * @param value The moved value
  61. */
  62. template<typename U, std::enable_if_t<list_contains_class<gp::remove_cvref_t<U>,T...>::value,int> = 0>
  63. fixed_variant(U&& value)
  64. : index{r_index_of<gp::remove_cvref_t<U>, T...>::value}
  65. {
  66. using actual = gp::remove_cvref_t<U>;
  67. dtor = gp::function<void(void*)>([](void* thing){
  68. ((actual*)thing)->~actual();
  69. }, nullopt);
  70. cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){
  71. new(dest) actual(*(actual*)src);
  72. }, nullopt);
  73. mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){
  74. new(dest) actual(gp::move(*(actual*)src));
  75. }, nullopt);
  76. mvtor((char*)&value, data);
  77. }
  78. fixed_variant(const fixed_variant& oth)
  79. : index{oth.index}
  80. , dtor{oth.dtor}
  81. , cpytor{oth.cpytor}
  82. , mvtor{oth.mvtor}
  83. {
  84. cpytor((char*)oth.data, (char*)data);
  85. }
  86. fixed_variant(fixed_variant& oth)
  87. : index{oth.index}
  88. , dtor{oth.dtor}
  89. , cpytor{oth.cpytor}
  90. , mvtor{oth.mvtor}
  91. {
  92. cpytor(oth.data, data);
  93. }
  94. fixed_variant(fixed_variant&& oth)
  95. : index{oth.index}
  96. , dtor{oth.dtor}
  97. , cpytor{oth.cpytor}
  98. , mvtor{oth.mvtor}
  99. {
  100. oth.index = std::numeric_limits<std::size_t>::max();
  101. mvtor(oth.data, data);
  102. }
  103. /**
  104. * @brief Gives an alternative (value usable in a switch statement) representing the given type
  105. *
  106. * @tparam U the type to match against
  107. * @return constexpr size_t a value that can be used in the case part of a switch case statement
  108. * @see type()
  109. */
  110. template<typename U>
  111. constexpr static size_t alt() {
  112. return r_index_of<U, T...>::value;
  113. }
  114. /**
  115. * @brief Gives the type as can be matched in a switch statement using alternatives
  116. *
  117. * @return size_t a value that can be used in the switch part of a switch case statement
  118. * @see alt()
  119. */
  120. size_t type() const {
  121. return index;
  122. }
  123. void operator=(fixed_variant& value)
  124. {
  125. if(index != std::numeric_limits<std::size_t>::max())
  126. {
  127. dtor((void*)data);
  128. }
  129. index = value.index;
  130. cpytor = value.cpytor;
  131. dtor = value.dtor;
  132. mvtor = value.mvtor;
  133. cpytor(value.data, data);
  134. }
  135. void operator=(fixed_variant&& value)
  136. {
  137. if(index != std::numeric_limits<std::size_t>::max())
  138. {
  139. dtor((void*)data);
  140. }
  141. dtor = value.dtor;
  142. cpytor = value.cpytor;
  143. mvtor = value.mvtor;
  144. index = value.index;
  145. value.index = std::numeric_limits<std::size_t>::max();
  146. mvtor(value.data, data);
  147. }
  148. template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
  149. void operator=(U& value)
  150. {
  151. if(index != std::numeric_limits<std::size_t>::max())
  152. {
  153. dtor((void*)data);
  154. }
  155. index = r_index_of<gp::remove_cvref_t<U>, T...>::value;
  156. new(data) U(value);
  157. dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt);
  158. cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt);
  159. mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(gp::move(*(U*)src));}, nullopt);
  160. }
  161. template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
  162. void operator=(U&& value)
  163. {
  164. if(index != std::numeric_limits<std::size_t>::max())
  165. {
  166. dtor((void*)data);
  167. }
  168. new(data) U(gp::move(value));
  169. index = r_index_of<gp::remove_cvref_t<U>, T...>::value;
  170. dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt);
  171. cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt);
  172. mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(gp::move(*(U*)src));}, nullopt);
  173. }
  174. ~fixed_variant()
  175. {
  176. if(index != std::numeric_limits<std::size_t>::max() && dtor.ready())
  177. {
  178. dtor((void*)data);
  179. index = std::numeric_limits<std::size_t>::max();
  180. }
  181. }
  182. /**
  183. * @brief Brutally decay the variant into the given type.
  184. Will throw (@see bad_variant_access) if exceptions are enabled, else behaviour is undefined.
  185. *
  186. * @tparam U the type to decay towards
  187. * @return constexpr U& a decayed reference to the variant
  188. */
  189. template<typename U>
  190. constexpr U& value()
  191. {
  192. if constexpr (gp_config::has_exceptions)
  193. {
  194. if(r_index_of<gp::remove_cvref_t<U>, T...>::value != index)
  195. {
  196. throw bad_variant_access<U>{};
  197. }
  198. }
  199. return *reinterpret_cast<U*>(data);
  200. }
  201. /**
  202. * @brief Tests if the variant is of a particular type.
  203. *
  204. * @tparam U the type to match against
  205. * @return true if the types match
  206. * @return false if the types don't match
  207. */
  208. template<typename U>
  209. constexpr bool is_a()
  210. {
  211. if(r_index_of<gp::remove_cvref_t<U>, T...>::value == index)
  212. {
  213. return true;
  214. }
  215. else
  216. {
  217. return false;
  218. }
  219. }
  220. };
  221. }