From 10940da3818e88f43e5532c56ae8b39cb05fc8ed Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 13 Oct 2023 18:00:28 +0200 Subject: [PATCH] util/Intrusive{List,HashSet}: add "tag" for base hooks --- src/util/IntrusiveHashSet.hxx | 12 +++-- src/util/IntrusiveList.hxx | 14 ++++-- test/util/TestIntrusiveHashSet.cxx | 71 ++++++++++++++++++++++++++++++ test/util/TestIntrusiveList.cxx | 45 +++++++++++++++++++ 4 files changed, 135 insertions(+), 7 deletions(-) diff --git a/src/util/IntrusiveHashSet.hxx b/src/util/IntrusiveHashSet.hxx index 9b7c38ac3..9a578566f 100644 --- a/src/util/IntrusiveHashSet.hxx +++ b/src/util/IntrusiveHashSet.hxx @@ -19,7 +19,11 @@ struct IntrusiveHashSetOptions { bool zero_initialized = false; }; -template +/** + * @param Tag an arbitrary tag type to allow using multiple base hooks + */ +template struct IntrusiveHashSetHook { using SiblingsHook = IntrusiveListHook; @@ -36,12 +40,14 @@ struct IntrusiveHashSetHook { /** * For classes which embed #IntrusiveHashSetHook as base class. + * + * @param Tag selector for which #IntrusiveHashSetHook to use */ -template +template struct IntrusiveHashSetBaseHookTraits { /* a never-called helper function which is used by _Cast() */ template - static constexpr IntrusiveHashSetHook _Identity(const IntrusiveHashSetHook &) noexcept; + static constexpr IntrusiveHashSetHook _Identity(const IntrusiveHashSetHook &) noexcept; /* another never-called helper function which "calls" _Identity(), implicitly casting the item to the diff --git a/src/util/IntrusiveList.hxx b/src/util/IntrusiveList.hxx index bed23cbf0..99bb211e2 100644 --- a/src/util/IntrusiveList.hxx +++ b/src/util/IntrusiveList.hxx @@ -35,9 +35,13 @@ struct IntrusiveListNode { } }; -template +/** + * @param Tag an arbitrary tag type to allow using multiple base hooks + */ +template class IntrusiveListHook { - template friend struct IntrusiveListBaseHookTraits; + template friend struct IntrusiveListBaseHookTraits; template friend struct IntrusiveListMemberHookTraits; template friend class IntrusiveList; @@ -91,12 +95,14 @@ using AutoUnlinkIntrusiveListHook = /** * For classes which embed #IntrusiveListHook as base class. + * + * @param Tag selector for which #IntrusiveHashSetHook to use */ -template +template struct IntrusiveListBaseHookTraits { /* a never-called helper function which is used by _Cast() */ template - static constexpr IntrusiveListHook _Identity(const IntrusiveListHook &) noexcept; + static constexpr IntrusiveListHook _Identity(const IntrusiveListHook &) noexcept; /* another never-called helper function which "calls" _Identity(), implicitly casting the item to the diff --git a/test/util/TestIntrusiveHashSet.cxx b/test/util/TestIntrusiveHashSet.cxx index 4a23ad798..b838073f3 100644 --- a/test/util/TestIntrusiveHashSet.cxx +++ b/test/util/TestIntrusiveHashSet.cxx @@ -149,3 +149,74 @@ TEST(IntrusiveHashSet, Multi) ASSERT_EQ(set.remove_and_dispose_key(b, [](auto*){}), 0U); ASSERT_EQ(set.find(b), set.end()); } + +TEST(IntrusiveHashSet, Tag) +{ + struct A {}; + struct B {}; + + struct TaggedItem final + : IntrusiveHashSetHook, + IntrusiveHashSetHook { + int a, b; + + TaggedItem(int _a, int _b) noexcept + :a(_a), b(_b) {} + }; + + struct GetA { + int operator()(const TaggedItem &item) const noexcept { + return item.a; + } + }; + + struct GetB { + int operator()(const TaggedItem &item) const noexcept { + return item.b; + } + }; + + TaggedItem one{1, 11}, two{2, 22}; + + IntrusiveHashSet, + std::equal_to, + GetA>, + IntrusiveHashSetBaseHookTraits> a; + + IntrusiveHashSet, + std::equal_to, + GetB>, + IntrusiveHashSetBaseHookTraits> b; + + EXPECT_TRUE(a.empty()); + EXPECT_TRUE(b.empty()); + + a.insert(one); + a.insert(two); + + EXPECT_FALSE(a.empty()); + EXPECT_TRUE(b.empty()); + + b.insert(one); + + EXPECT_FALSE(a.empty()); + EXPECT_FALSE(b.empty()); + + a.clear(); + + EXPECT_TRUE(a.empty()); + EXPECT_FALSE(b.empty()); + + a.insert(two); + a.insert(one); + + EXPECT_FALSE(a.empty()); + EXPECT_FALSE(b.empty()); + + b.erase(b.iterator_to(one)); + + EXPECT_FALSE(a.empty()); + EXPECT_TRUE(b.empty()); +} diff --git a/test/util/TestIntrusiveList.cxx b/test/util/TestIntrusiveList.cxx index 02b5fe73c..7fe52b320 100644 --- a/test/util/TestIntrusiveList.cxx +++ b/test/util/TestIntrusiveList.cxx @@ -166,6 +166,51 @@ TEST(IntrusiveList, AutoUnlink) ASSERT_TRUE(b.is_linked()); } +TEST(IntrusiveList, Tag) +{ + struct A {}; + struct B {}; + + struct TaggedItem + : public IntrusiveListHook, + public IntrusiveListHook {}; + + TaggedItem one, two; + + IntrusiveList> a; + IntrusiveList> b; + + EXPECT_TRUE(a.empty()); + EXPECT_TRUE(b.empty()); + + a.push_back(one); + a.push_back(two); + + EXPECT_FALSE(a.empty()); + EXPECT_TRUE(b.empty()); + + b.push_back(one); + + EXPECT_FALSE(a.empty()); + EXPECT_FALSE(b.empty()); + + a.clear(); + + EXPECT_TRUE(a.empty()); + EXPECT_FALSE(b.empty()); + + a.push_back(two); + a.push_back(one); + + EXPECT_FALSE(a.empty()); + EXPECT_FALSE(b.empty()); + + b.erase(b.iterator_to(one)); + + EXPECT_FALSE(a.empty()); + EXPECT_TRUE(b.empty()); +} + TEST(IntrusiveList, Merge) { using Item = CharItem;