#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;
|
|
}
|
|
}
|
|
};
|
|
}
|