Skip to content

Commit

Permalink
util/Intrusive{List,HashSet}: add "tag" for base hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxKellermann committed Oct 13, 2023
1 parent 7a5cf78 commit 8075055
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 7 deletions.
12 changes: 9 additions & 3 deletions src/util/IntrusiveHashSet.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand All @@ -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
Expand Down
14 changes: 10 additions & 4 deletions src/util/IntrusiveList.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
71 changes: 71 additions & 0 deletions test/util/TestIntrusiveHashSet.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
45 changes: 45 additions & 0 deletions test/util/TestIntrusiveList.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down

0 comments on commit 8075055

Please sign in to comment.