util/Intrusive{List,HashSet}: add "tag" for base hooks

This commit is contained in:
Max Kellermann 2023-10-13 18:00:28 +02:00 committed by Max Kellermann
parent 57d9cffc42
commit 10940da381
4 changed files with 135 additions and 7 deletions

View File

@ -19,7 +19,11 @@ struct IntrusiveHashSetOptions {
bool zero_initialized = false;
};
template<IntrusiveHookMode mode=IntrusiveHookMode::NORMAL>
/**
* @param Tag an arbitrary tag type to allow using multiple base hooks
*/
template<IntrusiveHookMode mode=IntrusiveHookMode::NORMAL,
typename Tag=void>
struct IntrusiveHashSetHook {
using SiblingsHook = IntrusiveListHook<mode>;
@ -36,12 +40,14 @@ struct IntrusiveHashSetHook {
/**
* For classes which embed #IntrusiveHashSetHook as base class.
*
* @param Tag selector for which #IntrusiveHashSetHook to use
*/
template<typename T>
template<typename T, typename Tag=void>
struct IntrusiveHashSetBaseHookTraits {
/* a never-called helper function which is used by _Cast() */
template<IntrusiveHookMode mode>
static constexpr IntrusiveHashSetHook<mode> _Identity(const IntrusiveHashSetHook<mode> &) noexcept;
static constexpr IntrusiveHashSetHook<mode, Tag> _Identity(const IntrusiveHashSetHook<mode, Tag> &) noexcept;
/* another never-called helper function which "calls"
_Identity(), implicitly casting the item to the

View File

@ -35,9 +35,13 @@ struct IntrusiveListNode {
}
};
template<IntrusiveHookMode _mode=IntrusiveHookMode::NORMAL>
/**
* @param Tag an arbitrary tag type to allow using multiple base hooks
*/
template<IntrusiveHookMode _mode=IntrusiveHookMode::NORMAL,
typename Tag=void>
class IntrusiveListHook {
template<typename T> friend struct IntrusiveListBaseHookTraits;
template<typename, typename> friend struct IntrusiveListBaseHookTraits;
template<auto member> friend struct IntrusiveListMemberHookTraits;
template<typename T, typename HookTraits, IntrusiveListOptions> 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<typename T>
template<typename T, typename Tag=void>
struct IntrusiveListBaseHookTraits {
/* a never-called helper function which is used by _Cast() */
template<IntrusiveHookMode mode>
static constexpr IntrusiveListHook<mode> _Identity(const IntrusiveListHook<mode> &) noexcept;
static constexpr IntrusiveListHook<mode, Tag> _Identity(const IntrusiveListHook<mode, Tag> &) noexcept;
/* another never-called helper function which "calls"
_Identity(), implicitly casting the item to the

View File

@ -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<IntrusiveHookMode::NORMAL, A>,
IntrusiveHashSetHook<IntrusiveHookMode::NORMAL, B> {
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<TaggedItem, 3,
IntrusiveHashSetOperators<std::hash<int>,
std::equal_to<int>,
GetA>,
IntrusiveHashSetBaseHookTraits<TaggedItem, A>> a;
IntrusiveHashSet<TaggedItem, 3,
IntrusiveHashSetOperators<std::hash<int>,
std::equal_to<int>,
GetB>,
IntrusiveHashSetBaseHookTraits<TaggedItem, B>> 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());
}

View File

@ -166,6 +166,51 @@ TEST(IntrusiveList, AutoUnlink)
ASSERT_TRUE(b.is_linked());
}
TEST(IntrusiveList, Tag)
{
struct A {};
struct B {};
struct TaggedItem
: public IntrusiveListHook<IntrusiveHookMode::NORMAL, A>,
public IntrusiveListHook<IntrusiveHookMode::NORMAL, B> {};
TaggedItem one, two;
IntrusiveList<TaggedItem, IntrusiveListBaseHookTraits<TaggedItem, A>> a;
IntrusiveList<TaggedItem, IntrusiveListBaseHookTraits<TaggedItem, B>> 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<IntrusiveHookMode::NORMAL>;