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:
parent
3038b6b7dc
commit
4fdbac236d
Notes:
sideshowbarker
2024-07-18 18:20:31 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/4fdbac236d8 Pull-request: https://github.com/SerenityOS/serenity/pull/6978 Reviewed-by: https://github.com/Quaker762 ✅ Reviewed-by: https://github.com/sunverwerth ✅
2 changed files with 66 additions and 3 deletions
62
AK/Variant.h
62
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<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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue