mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 03:55:24 +00:00
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 {
|
||||
|
@ -147,7 +203,7 @@ struct Empty {
|
|||
|
||||
template<typename... Ts>
|
||||
struct Variant
|
||||
: public Detail::VariantConstructors<Ts, Variant<Ts...>>... {
|
||||
: public Detail::MergeAndDeduplicatePacks<Detail::VariantConstructors<Ts, Variant<Ts...>>...> {
|
||||
private:
|
||||
using IndexType = Conditional<sizeof...(Ts) < 255, u8, size_t>; // 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<Ts, Variant<Ts...>>()...
|
||||
: Detail::MergeAndDeduplicatePacks<Detail::VariantConstructors<Ts, Variant<Ts...>>...>()
|
||||
, 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<Ts, Variant<Ts...>>::VariantConstructors...;
|
||||
using Detail::MergeAndDeduplicatePacks<Detail::VariantConstructors<Ts, Variant<Ts...>>...>::MergeAndDeduplicatePacks;
|
||||
|
||||
template<typename T>
|
||||
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;
|
||||
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