BAN: Variant now has variadic template types

This commit is contained in:
Bananymous 2023-04-18 18:29:48 +03:00
parent 5494e2c125
commit 04ac23b67c
2 changed files with 207 additions and 122 deletions

View File

@ -72,7 +72,7 @@ namespace BAN
: m_data(move(error)) : m_data(move(error))
{} {}
bool is_error() const { return m_data.template is<Error>(); } bool is_error() const { return m_data.template has<Error>(); }
const Error& error() const { return m_data.template get<Error>(); } const Error& error() const { return m_data.template get<Error>(); }
Error& error() { return m_data.template get<Error>(); } Error& error() { return m_data.template get<Error>(); }
const T& value() const { return m_data.template get<T>(); } const T& value() const { return m_data.template get<T>(); }

View File

@ -7,141 +7,226 @@
namespace BAN namespace BAN
{ {
template<typename T1, typename T2> namespace detail
class Variant
{ {
public:
static_assert(!is_same_v<T1, T2>);
Variant() = default; template<typename T>
constexpr size_t max_size() { return sizeof(T); }
template<typename T0, typename T1, typename... Ts>
constexpr size_t max_size() { return sizeof(T0) > sizeof(T1) ? max_size<T0, Ts...>() : max_size<T1, Ts...>(); }
Variant(const T1& value) { set(value); } template<typename T>
Variant(T1&& value) { set(move(value)); } constexpr size_t max_align() { return alignof(T); }
Variant(const T2& value) { set(value); } template<typename T0, typename T1, typename... Ts>
Variant(T2&& value) { set(move(value)); } constexpr size_t max_align() { return alignof(T0) > alignof(T1) ? max_align<T0, Ts...>() : max_align<T1, Ts...>(); }
Variant(const Variant<T1, T2>& other) { *this = other; } template<typename T, typename T0, typename... Ts>
Variant(Variant<T1, T2>&& other) { *this = move(other); } constexpr size_t index()
~Variant() { clear(); }
Variant<T1, T2>& operator=(const Variant<T1, T2>& other);
Variant<T1, T2>& operator=(Variant<T1, T2>&& other);
template<typename U>
bool is() const;
template<typename U>
void set(U&&);
template<typename U>
void set(const U& value) { set(move(U(value))); }
template<typename U>
const U& get() const;
template<typename U>
U& get();
void clear();
private:
static constexpr uint32_t m_size = Math::max(sizeof(T1), sizeof(T2));
alignas(Math::max(alignof(T1), alignof(T2))) uint8_t m_storage[m_size] = {};
uint32_t m_index = 0;
};
template<typename T1, typename T2>
Variant<T1, T2>& Variant<T1, T2>::operator=(const Variant<T1, T2>& other)
{ {
clear(); if constexpr(is_same_v<T, T0>)
if (other.is<T1>()) return 0;
set(other.get<T1>()); else if constexpr(sizeof...(Ts) == 0)
if (other.is<T2>()) return 1;
set(other.get<T2>()); else
return *this; return index<T, Ts...>() + 1;
} }
template<typename T1, typename T2> template<typename T, typename... Ts>
Variant<T1, T2>& Variant<T1, T2>::operator=(Variant<T1, T2>&& other) void destruct(size_t index, uint8_t* data)
{
if (index == 0)
reinterpret_cast<T*>(data)->~T();
else if constexpr(sizeof...(Ts) > 0)
destruct<Ts...>(index - 1, data);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void move_construct(size_t index, uint8_t* source, uint8_t* target)
{
if (index == 0)
new (target) T(move(*reinterpret_cast<T*>(source)));
else if constexpr(sizeof...(Ts) > 0)
move_construct<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void copy_construct(size_t index, const uint8_t* source, uint8_t* target)
{
if (index == 0)
new (target) T(*reinterpret_cast<const T*>(source));
else if constexpr(sizeof...(Ts) > 0)
copy_construct<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void move_assign(size_t index, uint8_t* source, uint8_t* target)
{
if (index == 0)
*reinterpret_cast<T*>(target) = move(*reinterpret_cast<T*>(source));
else if constexpr(sizeof...(Ts) > 0)
move_assign<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
template<typename T, typename... Ts>
void copy_assign(size_t index, const uint8_t* source, uint8_t* target)
{
if (index == 0)
*reinterpret_cast<T*>(target) = *reinterpret_cast<const T*>(source);
else if constexpr(sizeof...(Ts) > 0)
copy_assign<Ts...>(index - 1, source, target);
else
ASSERT_NOT_REACHED();
}
}
template<typename... Ts>
requires (!is_lvalue_reference_v<Ts> && ...)
class Variant
{
private:
template<typename T>
static constexpr bool can_have() { return detail::index<T, Ts...>() != invalid_index(); }
static constexpr size_t invalid_index() { return sizeof...(Ts); }
public:
Variant()
: m_index(invalid_index())
{ }
Variant(Variant&& other)
: m_index(other.m_index)
{
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
other.clear();
}
Variant(const Variant& other)
: m_index(other.m_index)
{
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
}
template<typename T>
Variant(T&& value) requires (can_have<T>())
: m_index(detail::index<T, Ts...>())
{
new (m_storage) T(move(value));
}
template<typename T>
Variant(const T& value) requires (can_have<T>())
: m_index(detail::index<T, Ts...>())
{
new (m_storage) T(value);
}
~Variant()
{ {
clear(); clear();
if (other.is<T1>()) }
set(move(other.get<T1>()));
if (other.is<T2>()) Variant& operator=(Variant&& other)
set(move(other.get<T2>())); {
if (m_index == other.m_index)
{
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
}
else
{
clear();
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
m_index = other.m_index;
}
other.clear(); other.clear();
return *this; return *this;
} }
template<typename T1, typename T2> Variant& operator=(const Variant& other)
template<typename U>
bool Variant<T1, T2>::is() const
{ {
if constexpr(is_same_v<T1, U>) if (m_index == other.m_index)
return m_index == 1; {
if constexpr(is_same_v<T2, U>) detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
return m_index == 2;
return false;
} }
else
template<typename T1, typename T2>
template<typename U>
void Variant<T1, T2>::set(U&& value)
{ {
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
clear(); clear();
if constexpr(is_same_v<T1, U>) detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
{ m_index = other.m_index;
new (m_storage) T1(move(value));
m_index = 1;
} }
if constexpr(is_same_v<T2, U>) return *this;
}
template<typename T>
Variant& operator=(T&& value) requires (can_have<T>())
{ {
new (m_storage) T2(move(value)); set(move(value));
m_index = 2; return *this;
}
template<typename T>
Variant& operator=(const T& value) requires (can_have<T>())
{
set(value);
return *this;
}
template<typename T>
bool has() const requires (can_have<T>())
{
return m_index == detail::index<T, Ts...>();
}
template<typename T>
void set(T&& value) requires (can_have<T>())
{
clear();
m_index = detail::index<T, Ts...>();
new (m_storage) T(move(value));
}
template<typename T>
void set(const T& value) requires (can_have<T>())
{
clear();
m_index = detail::index<T, Ts...>();
new (m_storage) T(value);
}
template<typename T>
T& get() requires (can_have<T>())
{
ASSERT(has<T>());
return (T&)m_storage;
}
template<typename T>
const T& get() const requires (can_have<T>())
{
ASSERT(has<T>());
return (const T&)m_storage;
}
void clear()
{
if (m_index != invalid_index())
{
detail::destruct<Ts...>(m_index, m_storage);
m_index = invalid_index();
} }
} }
template<typename T1, typename T2> private:
template<typename U> alignas(detail::max_align<Ts...>()) uint8_t m_storage[detail::max_size<Ts...>()] {};
const U& Variant<T1, T2>::get() const size_t m_index { invalid_index() };
{ };
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
if constexpr(is_same_v<T1, U>)
{
ASSERT(m_index == 1);
return *(T1*)m_storage;
}
if constexpr(is_same_v<T2, U>)
{
ASSERT(m_index == 2);
return *(T2*)m_storage;
}
}
template<typename T1, typename T2>
template<typename U>
U& Variant<T1, T2>::get()
{
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
if constexpr(is_same_v<T1, U>)
{
ASSERT(m_index == 1);
return *(T1*)m_storage;
}
if constexpr(is_same_v<T2, U>)
{
ASSERT(m_index == 2);
return *(T2*)m_storage;
}
}
template<typename T1, typename T2>
void Variant<T1, T2>::clear()
{
if (is<T1>()) ((T1*)m_storage)->~T1();
if (is<T2>()) ((T2*)m_storage)->~T2();
m_index = 0;
}
} }