AK/Variant: Deduplicate the contained types

This allows the construction of `Variant<int, int, int>`.
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.
This commit is contained in:
Ali Mohammad Pur 2021-05-09 08:22:43 +04:30 committed by Linus Groh
parent 3038b6b7dc
commit 4fdbac236d
Notes: sideshowbarker 2024-07-18 18:20:31 +09:00
2 changed files with 66 additions and 3 deletions

View file

@ -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<Ts...> is just a way to pass around the type parameter pack Ts
template<typename... Ts>
struct ParameterPack : ParameterPackTag {
};
// Blank<T> is a unique replacement for T, if T is a duplicate type.
template<typename T>
struct Blank {
};
template<typename A, typename P>
inline constexpr bool IsTypeInPack = false;
// IsTypeInPack<T, Pack<Ts...>> will just return whether 'T' exists in 'Ts'.
template<typename T, typename... Ts>
inline constexpr bool IsTypeInPack<T, ParameterPack<Ts...>> = (IsSame<T, Ts> || ...);
// Replaces T with Blank<T> if it exists in Qs.
template<typename T, typename... Qs>
using BlankIfDuplicate = Conditional<(IsTypeInPack<T, Qs> || ...), Blank<T>, T>;
template<unsigned I, typename...>
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<unsigned I, typename... Ts, unsigned... Js, typename... Qs>
struct InheritFromUniqueEntries<I, ParameterPack<Ts...>, IndexSequence<Js...>, Qs...>
: public BlankIfDuplicate<Ts, Conditional<Js <= I, ParameterPack<>, Qs>...>... {
using BlankIfDuplicate<Ts, Conditional<Js <= I, ParameterPack<>, Qs>...>::BlankIfDuplicate...;
};
template<typename...>
struct InheritFromPacks;
// InheritFromPacks will attempt to 'merge' the pack 'Ps' with *itself*, but skip the duplicate entries
// (via InheritFromUniqueEntries).
template<unsigned... Is, typename... Ps>
struct InheritFromPacks<IndexSequence<Is...>, Ps...>
: public InheritFromUniqueEntries<Is, Ps, IndexSequence<Is...>, Ps...>... {
using InheritFromUniqueEntries<Is, Ps, IndexSequence<Is...>, Ps...>::InheritFromUniqueEntries...;
};
// Just a nice wrapper around InheritFromPacks, which will wrap any parameter packs in ParameterPack (unless it alread is one).
template<typename... Ps>
using MergeAndDeduplicatePacks = InheritFromPacks<MakeIndexSequence<sizeof...(Ps)>, Conditional<IsBaseOf<ParameterPackTag, Ps>, Ps, ParameterPack<Ps>>...>;
} }
namespace AK { namespace AK {
@ -147,7 +203,7 @@ struct Empty {
template<typename... Ts> template<typename... Ts>
struct Variant struct Variant
: public Detail::VariantConstructors<Ts, Variant<Ts...>>... { : public Detail::MergeAndDeduplicatePacks<Detail::VariantConstructors<Ts, Variant<Ts...>>...> {
private: private:
using IndexType = Conditional<sizeof...(Ts) < 255, u8, size_t>; // Note: size+1 reserved for internal value checks using IndexType = Conditional<sizeof...(Ts) < 255, u8, size_t>; // Note: size+1 reserved for internal value checks
static constexpr IndexType invalid_index = sizeof...(Ts); 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 // 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. // but it will still contain the "moved-from" state of the object it previously contained.
Variant(Variant&& old) Variant(Variant&& old)
: Detail::VariantConstructors<Ts, Variant<Ts...>>()... : Detail::MergeAndDeduplicatePacks<Detail::VariantConstructors<Ts, Variant<Ts...>>...>()
, m_index(old.m_index) , m_index(old.m_index)
{ {
Helper::move_(old.m_index, old.m_data, m_data); Helper::move_(old.m_index, old.m_data, m_data);
@ -196,7 +252,7 @@ public:
return *this; return *this;
} }
using Detail::VariantConstructors<Ts, Variant<Ts...>>::VariantConstructors...; using Detail::MergeAndDeduplicatePacks<Detail::VariantConstructors<Ts, Variant<Ts...>>...>::MergeAndDeduplicatePacks;
template<typename T> template<typename T>
void set(T&& t) requires(index_of<T>() != invalid_index) void set(T&& t) requires(index_of<T>() != invalid_index)

View file

@ -110,3 +110,10 @@ TEST_CASE(moved_from_state)
auto same_contents = __builtin_memcmp(&bunch_of_values, &optionally_a_bunch_of_values.get<Vector<i32>>(), sizeof(bunch_of_values)) == 0; auto same_contents = __builtin_memcmp(&bunch_of_values, &optionally_a_bunch_of_values.get<Vector<i32>>(), sizeof(bunch_of_values)) == 0;
EXPECT(same_contents); EXPECT(same_contents);
} }
TEST_CASE(duplicated_types)
{
Variant<int, int, int, int> its_just_an_int { 42 };
EXPECT(its_just_an_int.has<int>());
EXPECT_EQ(its_just_an_int.get<int>(), 42);
}