From 4fdbac236d8adf042837850930c0be46df6c457f Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Sun, 9 May 2021 08:22:43 +0430 Subject: [PATCH] AK/Variant: Deduplicate the contained types This allows the construction of `Variant`. While this might not seem useful, it is very useful for making variants that contain a series of member function pointers, which I plan to use in LibGL for glGenLists() and co. --- AK/Variant.h | 62 ++++++++++++++++++++++++++++++++++++++-- Tests/AK/TestVariant.cpp | 7 +++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/AK/Variant.h b/AK/Variant.h index ad3448875b..0682ea6a1f 100644 --- a/AK/Variant.h +++ b/AK/Variant.h @@ -138,6 +138,62 @@ private: } }; +// Type list deduplication +// Since this is a big template mess, each template is commented with how and why it works. +struct ParameterPackTag { +}; + +// Pack is just a way to pass around the type parameter pack Ts +template +struct ParameterPack : ParameterPackTag { +}; + +// Blank is a unique replacement for T, if T is a duplicate type. +template +struct Blank { +}; + +template +inline constexpr bool IsTypeInPack = false; + +// IsTypeInPack> will just return whether 'T' exists in 'Ts'. +template +inline constexpr bool IsTypeInPack> = (IsSame || ...); + +// Replaces T with Blank if it exists in Qs. +template +using BlankIfDuplicate = Conditional<(IsTypeInPack || ...), Blank, T>; + +template +struct InheritFromUniqueEntries; + +// InheritFromUniqueEntries will inherit from both Qs and Ts, but only scan entries going *forwards* +// that is to say, if it's scanning from index I in Qs, it won't scan for duplicates for entries before I +// as that has already been checked before. +// This makes sure that the search is linear in time (like the 'merge' step of merge sort). +template +struct InheritFromUniqueEntries, IndexSequence, Qs...> + : public BlankIfDuplicate, Qs>...>... { + + using BlankIfDuplicate, Qs>...>::BlankIfDuplicate...; +}; + +template +struct InheritFromPacks; + +// InheritFromPacks will attempt to 'merge' the pack 'Ps' with *itself*, but skip the duplicate entries +// (via InheritFromUniqueEntries). +template +struct InheritFromPacks, Ps...> + : public InheritFromUniqueEntries, Ps...>... { + + using InheritFromUniqueEntries, Ps...>::InheritFromUniqueEntries...; +}; + +// Just a nice wrapper around InheritFromPacks, which will wrap any parameter packs in ParameterPack (unless it alread is one). +template +using MergeAndDeduplicatePacks = InheritFromPacks, Conditional, Ps, ParameterPack>...>; + } namespace AK { @@ -147,7 +203,7 @@ struct Empty { template struct Variant - : public Detail::VariantConstructors>... { + : public Detail::MergeAndDeduplicatePacks>...> { private: using IndexType = Conditional; // Note: size+1 reserved for internal value checks static constexpr IndexType invalid_index = sizeof...(Ts); @@ -171,7 +227,7 @@ public: // and if a variant with a nontrivial move ctor is moved from, it may or may not be valid // but it will still contain the "moved-from" state of the object it previously contained. Variant(Variant&& old) - : Detail::VariantConstructors>()... + : Detail::MergeAndDeduplicatePacks>...>() , m_index(old.m_index) { Helper::move_(old.m_index, old.m_data, m_data); @@ -196,7 +252,7 @@ public: return *this; } - using Detail::VariantConstructors>::VariantConstructors...; + using Detail::MergeAndDeduplicatePacks>...>::MergeAndDeduplicatePacks; template void set(T&& t) requires(index_of() != invalid_index) diff --git a/Tests/AK/TestVariant.cpp b/Tests/AK/TestVariant.cpp index 769cde605a..587eae2819 100644 --- a/Tests/AK/TestVariant.cpp +++ b/Tests/AK/TestVariant.cpp @@ -110,3 +110,10 @@ TEST_CASE(moved_from_state) auto same_contents = __builtin_memcmp(&bunch_of_values, &optionally_a_bunch_of_values.get>(), sizeof(bunch_of_values)) == 0; EXPECT(same_contents); } + +TEST_CASE(duplicated_types) +{ + Variant its_just_an_int { 42 }; + EXPECT(its_just_an_int.has()); + EXPECT_EQ(its_just_an_int.get(), 42); +}