diff --git a/AK/Variant.h b/AK/Variant.h index 87d3089c73..ad3448875b 100644 --- a/AK/Variant.h +++ b/AK/Variant.h @@ -9,72 +9,102 @@ #include #include #include -#include namespace AK::Detail { -template -struct Variant; +template +struct VariantIndexOf { + static_assert(DependentFalse, "Invalid VariantIndex instantiated"); +}; -template -struct Variant { - static void delete_(const std::type_info& id, void* data) +template +struct VariantIndexOf { + consteval IndexType operator()() { - if (id == typeid(F)) - bit_cast(data)->~F(); + if constexpr (IsSame) + return InitialIndex; else - Variant::delete_(id, data); - } - - static void move_(const std::type_info& old_id, void* old_data, void* new_data) - { - if (old_id == typeid(F)) - new (new_data) F(move(*bit_cast(old_data))); - else - Variant::move_(old_id, old_data, new_data); - } - - static void copy_(const std::type_info& old_id, const void* old_data, void* new_data) - { - if (old_id == typeid(F)) - new (new_data) F(*bit_cast(old_data)); - else - Variant::copy_(old_id, old_data, new_data); - } - - template - static void visit_(const std::type_info& id, void* data, Visitor&& visitor) - { - if (id == typeid(F)) - visitor(*bit_cast(data)); - else - Variant::visit_(id, data, forward(visitor)); - } - - template - static void visit_(const std::type_info& id, const void* data, Visitor&& visitor) - { - if (id == typeid(F)) - visitor(*bit_cast(data)); - else - Variant::visit_(id, data, forward(visitor)); + return VariantIndexOf {}(); } }; -template<> -struct Variant<> { - static void delete_(const std::type_info&, void*) { } - static void move_(const std::type_info&, void*, void*) { } - static void copy_(const std::type_info&, const void*, void*) { } +template +struct VariantIndexOf { + consteval IndexType operator()() { return InitialIndex; } +}; + +template +consteval IndexType index_of() +{ + return VariantIndexOf {}(); +} + +template +struct Variant; + +template +struct Variant { + static constexpr auto current_index = VariantIndexOf {}(); + static void delete_(IndexType id, void* data) + { + if (id == current_index) + bit_cast(data)->~F(); + else + Variant::delete_(id, data); + } + + static void move_(IndexType old_id, void* old_data, void* new_data) + { + if (old_id == current_index) + new (new_data) F(move(*bit_cast(old_data))); + else + Variant::move_(old_id, old_data, new_data); + } + + static void copy_(IndexType old_id, const void* old_data, void* new_data) + { + if (old_id == current_index) + new (new_data) F(*bit_cast(old_data)); + else + Variant::copy_(old_id, old_data, new_data); + } + template - static void visit_(const std::type_info&, void*, Visitor&&) { } + static void visit_(IndexType id, void* data, Visitor&& visitor) + { + if (id == current_index) + visitor(*bit_cast(data)); + else + Variant::visit_(id, data, forward(visitor)); + } + template - static void visit_(const std::type_info&, const void*, Visitor&&) { } + static void visit_(IndexType id, const void* data, Visitor&& visitor) + { + if (id == current_index) + visitor(*bit_cast(data)); + else + Variant::visit_(id, data, forward(visitor)); + } +}; + +template +struct Variant { + static void delete_(IndexType, void*) { } + static void move_(IndexType, void*, void*) { } + static void copy_(IndexType, const void*, void*) { } + template + static void visit_(IndexType, void*, Visitor&&) { } + template + static void visit_(IndexType, const void*, Visitor&&) { } }; struct VariantNoClearTag { explicit VariantNoClearTag() = default; }; +struct VariantConstructTag { + explicit VariantConstructTag() = default; +}; template struct VariantConstructors { @@ -118,15 +148,22 @@ struct Empty { template struct Variant : public Detail::VariantConstructors>... { +private: + using IndexType = Conditional; // Note: size+1 reserved for internal value checks + static constexpr IndexType invalid_index = sizeof...(Ts); + template + static constexpr IndexType index_of() { return Detail::index_of(); } + +public: template friend struct Variant; Variant(const Variant& old) : Detail::VariantConstructors>()... - , m_type_info(old.m_type_info) + , m_index(old.m_index) { - Helper::copy_(*old.m_type_info, old.m_data, m_data); + Helper::copy_(old.m_index, old.m_data, m_data); } // Note: A moved-from variant emulates the state of the object it contains @@ -135,129 +172,139 @@ struct Variant // but it will still contain the "moved-from" state of the object it previously contained. Variant(Variant&& old) : Detail::VariantConstructors>()... - , m_type_info(old.m_type_info) + , m_index(old.m_index) { - Helper::move_(*old.m_type_info, old.m_data, m_data); + Helper::move_(old.m_index, old.m_data, m_data); } ~Variant() { - Helper::delete_(*m_type_info, m_data); + Helper::delete_(m_index, m_data); } Variant& operator=(const Variant& other) { - m_type_info = other.m_type_info; - Helper::copy_(*other.m_type_info, other.m_data, m_data); + m_index = other.m_index; + Helper::copy_(other.m_index, other.m_data, m_data); return *this; } Variant& operator=(Variant&& other) { - m_type_info = other.m_type_info; - Helper::move_(*other.m_type_info, other.m_data, m_data); + m_index = other.m_index; + Helper::move_(other.m_index, other.m_data, m_data); return *this; } using Detail::VariantConstructors>::VariantConstructors...; template - void set(T&& t) + void set(T&& t) requires(index_of() != invalid_index) { - Helper::delete_(*m_type_info, m_data); - new (m_data) T(forward(t)); - m_type_info = &typeid(T); + using StrippedT = RemoveCV>; + constexpr auto new_index = index_of(); + Helper::delete_(m_index, m_data); + new (m_data) StrippedT(forward(t)); + m_index = new_index; } template void set(T&& t, Detail::VariantNoClearTag) { - new (m_data) T(forward(t)); - m_type_info = &typeid(T); + using StrippedT = RemoveCV>; + constexpr auto new_index = index_of(); + new (m_data) StrippedT(forward(t)); + m_index = new_index; } template T* get_pointer() { - if (typeid(T) == *m_type_info) - return reinterpret_cast(m_data); + if (index_of() == m_index) + return bit_cast(&m_data); return nullptr; } template - T& get() + [[gnu::noinline]] T& get() { - VERIFY(typeid(T) == *m_type_info); - return *reinterpret_cast(m_data); + VERIFY(has()); + return *bit_cast(&m_data); } template - const T* get_pointer() const + [[gnu::noinline]] const T* get_pointer() const { - if (typeid(T) == *m_type_info) - return reinterpret_cast(m_data); + if (index_of() == m_index) + return bit_cast(&m_data); return nullptr; } template - const T& get() const + [[gnu::noinline]] const T& get() const { - VERIFY(typeid(T) == *m_type_info); - return *reinterpret_cast(m_data); + VERIFY(has()); + return *bit_cast(&m_data); } template [[nodiscard]] bool has() const { - return typeid(T) == *m_type_info; + return index_of() == m_index; } template void visit(Fs&&... functions) { Visitor visitor { forward(functions)... }; - Helper::visit_(*m_type_info, m_data, visitor); + Helper::visit_(m_index, m_data, visitor); } template void visit(Fs&&... functions) const { Visitor visitor { forward(functions)... }; - Helper::visit_(*m_type_info, m_data, visitor); + Helper::visit_(m_index, m_data, visitor); } template Variant downcast() && { - VERIFY(covers()); - Variant instance { m_type_info }; - Helper::move_(*m_type_info, m_data, instance.m_data); + Variant instance { Variant::invalid_index, Detail::VariantConstructTag {} }; + visit([&](auto& value) { + if constexpr (Variant::template can_contain>>()) + instance.set(move(value), Detail::VariantNoClearTag {}); + }); + VERIFY(instance.m_index != instance.invalid_index); return instance; } template Variant downcast() & { - VERIFY(covers()); - Variant instance { m_type_info }; - Helper::copy_(*m_type_info, m_data, instance.m_data); + Variant instance { Variant::invalid_index, Detail::VariantConstructTag {} }; + visit([&](const auto& value) { + if constexpr (Variant::template can_contain>>()) + instance.set(value, Detail::VariantNoClearTag {}); + }); + VERIFY(instance.m_index != instance.invalid_index); return instance; } + template + static constexpr bool can_contain() + { + return index_of() != invalid_index; + } + private: static constexpr auto data_size = integer_sequence_generate_array(0, IntegerSequence()).max(); static constexpr auto data_alignment = integer_sequence_generate_array(0, IntegerSequence()).max(); - using Helper = Detail::Variant; + using Helper = Detail::Variant; - template - bool covers() const - { - return ((typeid(NewTs) == *m_type_info) || ...); - } - - explicit Variant(const std::type_info* type_info) - : Detail::VariantConstructors>()... - , m_type_info(type_info) + explicit Variant(IndexType index, Detail::VariantConstructTag) + : Detail::MergeAndDeduplicatePacks>...>() + , m_index(index) { } @@ -275,7 +322,7 @@ private: // Note: Make sure not to default-initialize! // VariantConstructors::VariantConstructors(T) will set this to the correct value // So default-constructing to anything will leave the first initialization with that value instead of the correct one. - const std::type_info* m_type_info; + IndexType m_index; }; }