|
|
- #pragma once
-
- #include "gp_config.hpp"
-
- #include <gp/algorithms/move.hpp>
- #include <gp/algorithms/tmp_manip.hpp>
- #include "gp/utils/allocators/dummy.hpp"
- #include "gp/exception.hpp"
- #include "gp/functional/function.hpp"
-
- #include <type_traits>
- #include <new>
-
- // TODO: Implement the allocating flavour of variant
-
- namespace gp{
-
- /**
- * @brief A form of variant that expect only classes whose size is strictly defined at compile time
- *
- * @tparam T The list of types accepted by the variant
- */
- template<typename ...T>
- class fixed_variant final {
- std::size_t index = std::numeric_limits<std::size_t>::max();
- alignas(gp::max(alignof(T)...)) char data[max_size<T...>()];
- gp::function<void(void*, void*)> cpytor = {[](void*, void*){}, nullopt};
- gp::function<void(void*, void*)> mvtor = {[](void*, void*){}, nullopt};
- gp::function<void(void*)> dtor = {[](void*){}, nullopt};
- static_assert(all_of_fixed_size<T...>::value, "not fixed");
-
- public:
- fixed_variant()
- : fixed_variant(typename first_of<T...>::type{})
- {}
-
- /**
- * @brief Construct a new fixed variant from an object that is legal in it
- *
- * @tparam U A type that belongs in the list of types the variant supports
- * @param value The copied value
- */
- template<typename U, std::enable_if_t<list_contains_class<gp::remove_cvref_t<U>,T...>::value,int> = 0>
- fixed_variant(U& value)
- : index{r_index_of<gp::remove_cvref_t<U>, T...>::value}
- {
- using actual = gp::remove_cvref_t<U>;
- dtor = gp::function<void(void*)>([](void* thing){
- ((actual*)thing)->~actual();
- }, nullopt);
- cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){
- new(dest) actual(*(actual*)src);
- }, nullopt);
- mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){
- new(dest) actual(gp::move(*(actual*)src));
- }, nullopt);
- cpytor((char*)&value, data);
- }
-
- static_assert(list_contains_class<int, int>::value, "list_contains_class doesn't work properly");
- static_assert(list_contains_class<int, char, int>::value, "list_contains_class doesn't work properly");
- static_assert(list_contains_class<int, int, char>::value, "list_contains_class doesn't work properly");
- static_assert(list_contains_class<int, char, int, char>::value, "list_contains_class doesn't work properly");
- static_assert(!list_contains_class<int, char, char>::value, "list_contains_class doesn't work properly");
-
- /**
- * @brief Construct a new fixed variant from an object that is legal in it by moving said object
- *
- * @tparam U A type that belongs in the list of types the variant supports
- * @param value The moved value
- */
- template<typename U, std::enable_if_t<list_contains_class<gp::remove_cvref_t<U>,T...>::value,int> = 0>
- fixed_variant(U&& value)
- : index{r_index_of<gp::remove_cvref_t<U>, T...>::value}
- {
- using actual = gp::remove_cvref_t<U>;
- dtor = gp::function<void(void*)>([](void* thing){
- ((actual*)thing)->~actual();
- }, nullopt);
- cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){
- new(dest) actual(*(actual*)src);
- }, nullopt);
- mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){
- new(dest) actual(gp::move(*(actual*)src));
- }, nullopt);
- mvtor((char*)&value, data);
- }
-
- fixed_variant(const fixed_variant& oth)
- : index{oth.index}
- , dtor{oth.dtor}
- , cpytor{oth.cpytor}
- , mvtor{oth.mvtor}
- {
- cpytor((char*)oth.data, (char*)data);
- }
-
- fixed_variant(fixed_variant& oth)
- : index{oth.index}
- , dtor{oth.dtor}
- , cpytor{oth.cpytor}
- , mvtor{oth.mvtor}
- {
- cpytor(oth.data, data);
- }
-
- fixed_variant(fixed_variant&& oth)
- : index{oth.index}
- , dtor{oth.dtor}
- , cpytor{oth.cpytor}
- , mvtor{oth.mvtor}
- {
- oth.index = std::numeric_limits<std::size_t>::max();
- mvtor(oth.data, data);
- }
-
- /**
- * @brief Gives an alternative (value usable in a switch statement) representing the given type
- *
- * @tparam U the type to match against
- * @return constexpr size_t a value that can be used in the case part of a switch case statement
- * @see type()
- */
- template<typename U>
- constexpr static size_t alt() {
- return r_index_of<U, T...>::value;
- }
-
- /**
- * @brief Gives the type as can be matched in a switch statement using alternatives
- *
- * @return size_t a value that can be used in the switch part of a switch case statement
- * @see alt()
- */
- size_t type() const {
- return index;
- }
-
- void operator=(fixed_variant& value)
- {
- if(index != std::numeric_limits<std::size_t>::max())
- {
- dtor((void*)data);
- }
- index = value.index;
- cpytor = value.cpytor;
- dtor = value.dtor;
- mvtor = value.mvtor;
- cpytor(value.data, data);
- }
-
- void operator=(fixed_variant&& value)
- {
- if(index != std::numeric_limits<std::size_t>::max())
- {
- dtor((void*)data);
- }
- dtor = value.dtor;
- cpytor = value.cpytor;
- mvtor = value.mvtor;
- index = value.index;
- value.index = std::numeric_limits<std::size_t>::max();
- mvtor(value.data, data);
- }
-
- template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
- void operator=(U& value)
- {
- if(index != std::numeric_limits<std::size_t>::max())
- {
- dtor((void*)data);
- }
- index = r_index_of<gp::remove_cvref_t<U>, T...>::value;
- new(data) U(value);
- dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt);
- cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt);
- mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(gp::move(*(U*)src));}, nullopt);
- }
-
- template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
- void operator=(U&& value)
- {
- if(index != std::numeric_limits<std::size_t>::max())
- {
- dtor((void*)data);
- }
- new(data) U(gp::move(value));
- index = r_index_of<gp::remove_cvref_t<U>, T...>::value;
- dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt);
- cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt);
- mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(gp::move(*(U*)src));}, nullopt);
- }
-
- ~fixed_variant()
- {
- if(index != std::numeric_limits<std::size_t>::max() && dtor.ready())
- {
- dtor((void*)data);
- index = std::numeric_limits<std::size_t>::max();
- }
- }
-
- /**
- * @brief Brutally decay the variant into the given type.
- Will throw (@see bad_variant_access) if exceptions are enabled, else behaviour is undefined.
- *
- * @tparam U the type to decay towards
- * @return constexpr U& a decayed reference to the variant
- */
- template<typename U>
- constexpr U& value()
- {
- if constexpr (gp_config::has_exceptions)
- {
- if(r_index_of<gp::remove_cvref_t<U>, T...>::value != index)
- {
- throw bad_variant_access<U>{};
- }
- }
- return *reinterpret_cast<U*>(data);
- }
-
- /**
- * @brief Tests if the variant is of a particular type.
- *
- * @tparam U the type to match against
- * @return true if the types match
- * @return false if the types don't match
- */
- template<typename U>
- constexpr bool is_a()
- {
- if(r_index_of<gp::remove_cvref_t<U>, T...>::value == index)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- };
- }
|