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.

327 lines
9.4 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. #pragma once
  2. #include "gp_config.hpp"
  3. #include <gp/algorithm/move.hpp>
  4. #include <gp/algorithm/tmp_manip.hpp>
  5. #include "gp/allocator/dummy.hpp"
  6. #include "gp/exception.hpp"
  7. #include "gp/function.hpp"
  8. #include "gp/memory.hpp"
  9. #include <type_traits>
  10. #include <new>
  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. char buffer[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, buffer);
  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, buffer);
  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.buffer, (char*)buffer);
  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.buffer, buffer);
  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.buffer, buffer);
  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*)buffer);
  128. }
  129. index = value.index;
  130. cpytor = value.cpytor;
  131. dtor = value.dtor;
  132. mvtor = value.mvtor;
  133. cpytor(value.buffer, buffer);
  134. }
  135. void operator=(fixed_variant&& value)
  136. {
  137. if(index != std::numeric_limits<std::size_t>::max())
  138. {
  139. dtor((void*)buffer);
  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.buffer, buffer);
  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*)buffer);
  154. }
  155. index = r_index_of<gp::remove_cvref_t<U>, T...>::value;
  156. new(buffer) 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*)buffer);
  167. }
  168. new(buffer) 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*)buffer);
  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*>(buffer);
  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. /*template<typename allocator_t = gp_config::memory_module::default_allocator, typename ...T>
  222. class variant{
  223. std::size_t index = std::numeric_limits<std::size_t>::max();
  224. void* ptr;
  225. gp::function<void(void*)> dtor = [](void*){};
  226. allocator_t allocator;
  227. public:
  228. template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
  229. constexpr variant(U& value)
  230. : index{r_index_of<U, T...>::value}
  231. {
  232. ptr = (void*)new(allocator.allocate(sizeof(U))) U(value);
  233. dtor = gp::function([](void* thing){((U*)thing)->~U();}); // TODO:replae with delete(p,t)
  234. }
  235. template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
  236. constexpr variant(U&& value)
  237. : index{r_index_of<U, T...>::value}
  238. {
  239. ptr = (void*)new(allocator.allocate(sizeof(U))) U(std::move(value));
  240. dtor = gp::function([](void* thing){((U*)thing)->~U();}); // TODO:replae with delete(p,t)
  241. }
  242. template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
  243. void operator=(U& value)
  244. {
  245. if(index != std::numeric_limits<std::size_t>::max())
  246. {
  247. dtor(ptr);
  248. allocator.deallocate(ptr);
  249. }
  250. index = r_index_of<U, T...>::value;
  251. ptr = (void*)new(allocator.allocate(sizeof(U))) U(value);
  252. dtor = gp::function([](void* thing){((U*)thing)->~U();}); // TODO:replae with delete(p,t)
  253. }
  254. template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
  255. void operator=(U&& value)
  256. {
  257. if(index != std::numeric_limits<std::size_t>::max())
  258. {
  259. dtor(ptr);
  260. allocator.deallocate(ptr);
  261. }
  262. index = r_index_of<U, T...>::value;
  263. ptr = (void*)new(allocator.allocate(sizeof(U))) U(std::move(value));
  264. dtor = gp::function([](void* thing){((U*)thing)->~U();}); // TODO: replace with delete(p, t)
  265. }
  266. ~variant()
  267. {
  268. if(index != std::numeric_limits<std::size_t>::max())
  269. {
  270. dtor(ptr);
  271. allocator.deallocate(ptr);
  272. }
  273. }
  274. template<typename U>
  275. constexpr U& value()
  276. {
  277. if constexpr (gp_config::has_exceptions)
  278. {
  279. if(r_index_of<U, T...>::value != index)
  280. {
  281. throw bad_variant_access<U>{};
  282. }
  283. }
  284. return *reinterpret_cast<U*>(ptr);
  285. }
  286. template<typename U>
  287. constexpr U& is_a()
  288. {
  289. if(r_index_of<U, T...>::value == index)
  290. {
  291. return true;
  292. }
  293. else
  294. {
  295. return false;
  296. }
  297. }
  298. };*/
  299. }