From b41a8e2829490fc7a9b0eff64291af17a4f0efe1 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 18 Apr 2023 18:29:48 +0300 Subject: [PATCH] BAN: Variant now has variadic template types --- BAN/include/BAN/Errors.h | 2 +- BAN/include/BAN/Variant.h | 327 ++++++++++++++++++++++++-------------- 2 files changed, 207 insertions(+), 122 deletions(-) diff --git a/BAN/include/BAN/Errors.h b/BAN/include/BAN/Errors.h index 86dc7675aa..63674add32 100644 --- a/BAN/include/BAN/Errors.h +++ b/BAN/include/BAN/Errors.h @@ -72,7 +72,7 @@ namespace BAN : m_data(move(error)) {} - bool is_error() const { return m_data.template is(); } + bool is_error() const { return m_data.template has(); } const Error& error() const { return m_data.template get(); } Error& error() { return m_data.template get(); } const T& value() const { return m_data.template get(); } diff --git a/BAN/include/BAN/Variant.h b/BAN/include/BAN/Variant.h index b770126225..ba410e2888 100644 --- a/BAN/include/BAN/Variant.h +++ b/BAN/include/BAN/Variant.h @@ -7,141 +7,226 @@ namespace BAN { - template + namespace detail + { + + template + constexpr size_t max_size() { return sizeof(T); } + template + constexpr size_t max_size() { return sizeof(T0) > sizeof(T1) ? max_size() : max_size(); } + + template + constexpr size_t max_align() { return alignof(T); } + template + constexpr size_t max_align() { return alignof(T0) > alignof(T1) ? max_align() : max_align(); } + + template + constexpr size_t index() + { + if constexpr(is_same_v) + return 0; + else if constexpr(sizeof...(Ts) == 0) + return 1; + else + return index() + 1; + } + + template + void destruct(size_t index, uint8_t* data) + { + if (index == 0) + reinterpret_cast(data)->~T(); + else if constexpr(sizeof...(Ts) > 0) + destruct(index - 1, data); + else + ASSERT_NOT_REACHED(); + } + + template + void move_construct(size_t index, uint8_t* source, uint8_t* target) + { + if (index == 0) + new (target) T(move(*reinterpret_cast(source))); + else if constexpr(sizeof...(Ts) > 0) + move_construct(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + template + void copy_construct(size_t index, const uint8_t* source, uint8_t* target) + { + if (index == 0) + new (target) T(*reinterpret_cast(source)); + else if constexpr(sizeof...(Ts) > 0) + copy_construct(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + template + void move_assign(size_t index, uint8_t* source, uint8_t* target) + { + if (index == 0) + *reinterpret_cast(target) = move(*reinterpret_cast(source)); + else if constexpr(sizeof...(Ts) > 0) + move_assign(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + template + void copy_assign(size_t index, const uint8_t* source, uint8_t* target) + { + if (index == 0) + *reinterpret_cast(target) = *reinterpret_cast(source); + else if constexpr(sizeof...(Ts) > 0) + copy_assign(index - 1, source, target); + else + ASSERT_NOT_REACHED(); + } + + } + + template + requires (!is_lvalue_reference_v && ...) class Variant { + private: + template + static constexpr bool can_have() { return detail::index() != invalid_index(); } + static constexpr size_t invalid_index() { return sizeof...(Ts); } + public: - static_assert(!is_same_v); + Variant() + : m_index(invalid_index()) + { } - Variant() = default; + Variant(Variant&& other) + : m_index(other.m_index) + { + detail::move_construct(other.m_index, other.m_storage, m_storage); + other.clear(); + } - Variant(const T1& value) { set(value); } - Variant(T1&& value) { set(move(value)); } - Variant(const T2& value) { set(value); } - Variant(T2&& value) { set(move(value)); } + Variant(const Variant& other) + : m_index(other.m_index) + { + detail::copy_construct(other.m_index, other.m_storage, m_storage); + } - Variant(const Variant& other) { *this = other; } - Variant(Variant&& other) { *this = move(other); } + template + Variant(T&& value) requires (can_have()) + : m_index(detail::index()) + { + new (m_storage) T(move(value)); + } - ~Variant() { clear(); } + template + Variant(const T& value) requires (can_have()) + : m_index(detail::index()) + { + new (m_storage) T(value); + } - Variant& operator=(const Variant& other); - Variant& operator=(Variant&& other); + ~Variant() + { + clear(); + } - template - bool is() const; + Variant& operator=(Variant&& other) + { + if (m_index == other.m_index) + { + detail::move_assign(m_index, other.m_storage, m_storage); + } + else + { + clear(); + detail::move_construct(other.m_index, other.m_storage, m_storage); + m_index = other.m_index; + } + other.clear(); + return *this; + } - template - void set(U&&); - template - void set(const U& value) { set(move(U(value))); } + Variant& operator=(const Variant& other) + { + if (m_index == other.m_index) + { + detail::copy_assign(m_index, other.m_storage, m_storage); + } + else + { + clear(); + detail::copy_construct(other.m_index, other.m_storage, m_storage); + m_index = other.m_index; + } + return *this; + } - template - const U& get() const; - template - U& get(); + template + Variant& operator=(T&& value) requires (can_have()) + { + set(move(value)); + return *this; + } - void clear(); + template + Variant& operator=(const T& value) requires (can_have()) + { + set(value); + return *this; + } + + template + bool has() const requires (can_have()) + { + return m_index == detail::index(); + } + + template + void set(T&& value) requires (can_have()) + { + clear(); + m_index = detail::index(); + new (m_storage) T(move(value)); + } + + template + void set(const T& value) requires (can_have()) + { + clear(); + m_index = detail::index(); + new (m_storage) T(value); + } + + template + T& get() requires (can_have()) + { + ASSERT(has()); + return (T&)m_storage; + } + + template + const T& get() const requires (can_have()) + { + ASSERT(has()); + return (const T&)m_storage; + } + + void clear() + { + if (m_index != invalid_index()) + { + detail::destruct(m_index, m_storage); + m_index = invalid_index(); + } + } 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; + alignas(detail::max_align()) uint8_t m_storage[detail::max_size()] {}; + size_t m_index { invalid_index() }; }; - template - Variant& Variant::operator=(const Variant& other) - { - clear(); - if (other.is()) - set(other.get()); - if (other.is()) - set(other.get()); - return *this; - } - - template - Variant& Variant::operator=(Variant&& other) - { - clear(); - if (other.is()) - set(move(other.get())); - if (other.is()) - set(move(other.get())); - other.clear(); - return *this; - } - - template - template - bool Variant::is() const - { - if constexpr(is_same_v) - return m_index == 1; - if constexpr(is_same_v) - return m_index == 2; - return false; - } - - - template - template - void Variant::set(U&& value) - { - static_assert(is_same_v || is_same_v); - clear(); - if constexpr(is_same_v) - { - new (m_storage) T1(move(value)); - m_index = 1; - } - if constexpr(is_same_v) - { - new (m_storage) T2(move(value)); - m_index = 2; - } - } - - template - template - const U& Variant::get() const - { - static_assert(is_same_v || is_same_v); - if constexpr(is_same_v) - { - ASSERT(m_index == 1); - return *(T1*)m_storage; - } - if constexpr(is_same_v) - { - ASSERT(m_index == 2); - return *(T2*)m_storage; - } - } - - template - template - U& Variant::get() - { - static_assert(is_same_v || is_same_v); - if constexpr(is_same_v) - { - ASSERT(m_index == 1); - return *(T1*)m_storage; - } - if constexpr(is_same_v) - { - ASSERT(m_index == 2); - return *(T2*)m_storage; - } - } - - template - void Variant::clear() - { - if (is()) ((T1*)m_storage)->~T1(); - if (is()) ((T2*)m_storage)->~T2(); - m_index = 0; - } - } \ No newline at end of file