util/IntrusiveList: add enum LinkMode

Compile-time code simplification.
This commit is contained in:
Max Kellermann
2022-11-12 08:52:58 +01:00
parent 3023816491
commit fb5d77158a
14 changed files with 132 additions and 81 deletions

View File

@@ -30,6 +30,7 @@
#pragma once
#include "Cast.hxx"
#include "IntrusiveHookMode.hxx"
#include "MemberPointer.hxx"
#include "OptionalCounter.hxx"
@@ -47,6 +48,7 @@ struct IntrusiveListNode {
}
};
template<IntrusiveHookMode _mode=IntrusiveHookMode::NORMAL>
class IntrusiveListHook {
template<typename T> friend struct IntrusiveListBaseHookTraits;
template<auto member> friend struct IntrusiveListMemberHookTraits;
@@ -56,13 +58,33 @@ protected:
IntrusiveListNode siblings;
public:
IntrusiveListHook() noexcept = default;
static constexpr IntrusiveHookMode mode = _mode;
IntrusiveListHook() noexcept {
if constexpr (mode >= IntrusiveHookMode::TRACK)
siblings.next = nullptr;
}
~IntrusiveListHook() noexcept {
if constexpr (mode >= IntrusiveHookMode::AUTO_UNLINK)
if (is_linked())
unlink();
}
IntrusiveListHook(const IntrusiveListHook &) = delete;
IntrusiveListHook &operator=(const IntrusiveListHook &) = delete;
void unlink() noexcept {
IntrusiveListNode::Connect(*siblings.prev, *siblings.next);
if constexpr (mode >= IntrusiveHookMode::TRACK)
siblings.next = nullptr;
}
bool is_linked() const noexcept {
static_assert(mode >= IntrusiveHookMode::TRACK);
return siblings.next != nullptr;
}
private:
@@ -75,52 +97,27 @@ private:
}
};
/**
* A variant of #IntrusiveListHook which keeps track of whether it is
* currently in a list.
*/
class SafeLinkIntrusiveListHook : public IntrusiveListHook {
public:
SafeLinkIntrusiveListHook() noexcept {
siblings.next = nullptr;
}
void unlink() noexcept {
IntrusiveListHook::unlink();
siblings.next = nullptr;
}
bool is_linked() const noexcept {
return siblings.next != nullptr;
}
};
using SafeLinkIntrusiveListHook =
IntrusiveListHook<IntrusiveHookMode::TRACK>;
using AutoUnlinkIntrusiveListHook =
IntrusiveListHook<IntrusiveHookMode::AUTO_UNLINK>;
/**
* A variant of #IntrusiveListHook which auto-unlinks itself from the
* list upon destruction. As a side effect, it has an is_linked()
* method.
*/
class AutoUnlinkIntrusiveListHook : public SafeLinkIntrusiveListHook {
public:
~AutoUnlinkIntrusiveListHook() noexcept {
if (is_linked())
unlink();
}
};
/**
* Detect the hook type; this is important because
* SafeLinkIntrusiveListHook::unlink() needs to clear the "next"
* pointer. This is a template to postpone the type checks, to allow
* Detect the hook type which is embedded in the given type as a base
* class. This is a template to postpone the type checks, to allow
* forward-declared types.
*/
template<typename U>
struct IntrusiveListHookDetection {
static_assert(std::is_base_of_v<IntrusiveListHook, U>);
using type = std::conditional_t<std::is_base_of_v<SafeLinkIntrusiveListHook, U>,
SafeLinkIntrusiveListHook,
IntrusiveListHook>;
/* TODO can this be simplified somehow, without checking for
all possible enum values? */
using type = std::conditional_t<std::is_base_of_v<IntrusiveListHook<IntrusiveHookMode::NORMAL>, U>,
IntrusiveListHook<IntrusiveHookMode::NORMAL>,
std::conditional_t<std::is_base_of_v<IntrusiveListHook<IntrusiveHookMode::TRACK>, U>,
IntrusiveListHook<IntrusiveHookMode::TRACK>,
std::conditional_t<std::is_base_of_v<IntrusiveListHook<IntrusiveHookMode::AUTO_UNLINK>, U>,
IntrusiveListHook<IntrusiveHookMode::AUTO_UNLINK>,
void>>>;
};
/**
@@ -131,10 +128,6 @@ struct IntrusiveListBaseHookTraits {
template<typename U>
using Hook = typename IntrusiveListHookDetection<U>::type;
static constexpr bool IsAutoUnlink() noexcept {
return std::is_base_of_v<AutoUnlinkIntrusiveListHook, T>;
}
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
auto *hook = &Hook<T>::Cast(*node);
return static_cast<T *>(hook);
@@ -152,14 +145,12 @@ template<auto member>
struct IntrusiveListMemberHookTraits {
using T = MemberPointerContainerType<decltype(member)>;
using _Hook = MemberPointerType<decltype(member)>;
using Hook = typename IntrusiveListHookDetection<_Hook>::type;
static constexpr bool IsAutoUnlink() noexcept {
return std::is_base_of_v<AutoUnlinkIntrusiveListHook, _Hook>;
}
template<typename Dummy>
using Hook = _Hook;
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
auto &hook = Hook::Cast(*node);
auto &hook = Hook<T>::Cast(*node);
return &ContainerCast(hook, member);
}
@@ -176,14 +167,15 @@ template<typename T,
typename HookTraits=IntrusiveListBaseHookTraits<T>,
bool constant_time_size=false>
class IntrusiveList {
template<typename U>
using Hook = typename IntrusiveListHookDetection<U>::type;
IntrusiveListNode head{&head, &head};
[[no_unique_address]]
OptionalCounter<constant_time_size> counter;
static constexpr auto GetHookMode() noexcept {
return HookTraits::template Hook<T>::mode;
}
static constexpr T *Cast(IntrusiveListNode *node) noexcept {
return HookTraits::Cast(node);
}
@@ -234,7 +226,7 @@ public:
}
~IntrusiveList() noexcept {
if constexpr (std::is_base_of_v<SafeLinkIntrusiveListHook, T>)
if constexpr (GetHookMode() >= IntrusiveHookMode::TRACK)
clear();
}
@@ -283,7 +275,7 @@ public:
}
void clear() noexcept {
if constexpr (std::is_base_of_v<SafeLinkIntrusiveListHook, T>) {
if constexpr (GetHookMode() >= IntrusiveHookMode::TRACK) {
/* for SafeLinkIntrusiveListHook, we need to
remove each item manually, or else its
is_linked() method will not work */
@@ -504,7 +496,7 @@ public:
void insert(iterator p, reference t) noexcept {
static_assert(!constant_time_size ||
!HookTraits::IsAutoUnlink(),
GetHookMode() < IntrusiveHookMode::AUTO_UNLINK,
"Can't use auto-unlink hooks with constant_time_size");
auto &existing_node = ToNode(*p);