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.
 
 

292 lines
9.0 KiB

#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};
/**
* This is kinda bad, but stems from my inability to make gp::function work properly with constness
*/
bool(*cmptor)(const void*, const void*) = +[](const void*, const void*) -> bool {return false;};
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);
cmptor = +[](const void* lhs, const void* rhs) {
return *reinterpret_cast<const U*>(lhs) == *reinterpret_cast<const U*>(rhs);
};
}
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);
cmptor = +[](const void* lhs, const void* rhs) {
return *reinterpret_cast<const U*>(lhs) == *reinterpret_cast<const U*>(rhs);
};
}
fixed_variant(const fixed_variant& oth)
: index{oth.index}
, dtor{oth.dtor}
, cpytor{oth.cpytor}
, mvtor{oth.mvtor}
, cmptor{oth.cmptor}
{
cpytor((char*)oth.data, (char*)data);
}
fixed_variant(fixed_variant& oth)
: index{oth.index}
, dtor{oth.dtor}
, cpytor{oth.cpytor}
, mvtor{oth.mvtor}
, cmptor{oth.cmptor}
{
cpytor(oth.data, data);
}
fixed_variant(fixed_variant&& oth)
: index{oth.index}
, dtor{oth.dtor}
, cpytor{oth.cpytor}
, mvtor{oth.mvtor}
, cmptor{oth.cmptor}
{
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);
cmptor = value.cmptor;
}
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);
cmptor = value.cmptor;
}
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);
cmptor = +[](const void* lhs, const void* rhs) {
return *reinterpret_cast<const U*>(lhs) == *reinterpret_cast<const U*>(rhs);
};
}
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);
cmptor = +[](const void* lhs, const void* rhs) {
return *reinterpret_cast<const U*>(lhs) == *reinterpret_cast<const U*>(rhs);
};
}
~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 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 const U& value() const
{
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<const 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() const
{
if(r_index_of<gp::remove_cvref_t<U>, T...>::value == index)
{
return true;
}
else
{
return false;
}
}
bool operator==(const fixed_variant& rhs) const {
if(index != rhs.index) {
return false;
}
// TODO: find a way to remove those const casts, the underlying things also use const so it is safe but kind ew
return cmptor(reinterpret_cast<const void*>(const_cast<const fixed_variant*const>(this)), reinterpret_cast<const void*>(const_cast<const fixed_variant*const>(&rhs)));
}
};
}