util/IntrusiveList: add enum LinkMode
Compile-time code simplification.
This commit is contained in:
parent
3023816491
commit
fb5d77158a
|
@ -46,7 +46,7 @@ class RemoteTagCache final {
|
|||
|
||||
struct Item final
|
||||
: public boost::intrusive::unordered_set_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>,
|
||||
public IntrusiveListHook,
|
||||
public IntrusiveListHook<>,
|
||||
RemoteTagHandler
|
||||
{
|
||||
RemoteTagCache &parent;
|
||||
|
|
|
@ -53,7 +53,7 @@ class Client final
|
|||
friend struct ClientPerPartitionListHook;
|
||||
friend class ClientList;
|
||||
|
||||
IntrusiveListHook list_siblings, partition_siblings;
|
||||
IntrusiveListHook<> list_siblings, partition_siblings;
|
||||
|
||||
CoarseTimerEvent timeout_event;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ static constexpr unsigned DEVICE_PLAYLIST = -3;
|
|||
|
||||
class SongFilter;
|
||||
|
||||
struct Directory : IntrusiveListHook {
|
||||
struct Directory : IntrusiveListHook<> {
|
||||
/* Note: the #IntrusiveListHook is protected with the global
|
||||
#db_mutex. Read access in the update thread does not need
|
||||
protection. */
|
||||
|
|
|
@ -39,7 +39,7 @@ class ArchiveFile;
|
|||
* A song file inside the configured music directory. Internal
|
||||
* #SimpleDatabase class.
|
||||
*/
|
||||
struct Song : IntrusiveListHook {
|
||||
struct Song : IntrusiveListHook<> {
|
||||
/* Note: the #IntrusiveListHook is protected with the global
|
||||
#db_mutex. Read access in the update thread does not need
|
||||
protection. */
|
||||
|
|
|
@ -40,7 +40,9 @@ class EventLoop;
|
|||
* thread that runs the #EventLoop, except where explicitly documented
|
||||
* as thread-safe.
|
||||
*/
|
||||
class SocketEvent final : IntrusiveListHook, public EventPollBackendEvents
|
||||
class SocketEvent final
|
||||
: IntrusiveListHook<IntrusiveHookMode::NORMAL>,
|
||||
public EventPollBackendEvents
|
||||
{
|
||||
friend class EventLoop;
|
||||
friend struct IntrusiveListBaseHookTraits<SocketEvent>;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
* A lease for an #InputCacheItem.
|
||||
*/
|
||||
class InputCacheLease
|
||||
: public IntrusiveListHook
|
||||
: public IntrusiveListHook<>
|
||||
{
|
||||
InputCacheItem *item = nullptr;
|
||||
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
|
||||
namespace Uring {
|
||||
|
||||
class CancellableOperation : public IntrusiveListHook
|
||||
class CancellableOperation
|
||||
: public IntrusiveListHook<IntrusiveHookMode::NORMAL>
|
||||
{
|
||||
Operation *operation;
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
template<typename T>
|
||||
class CancellablePointer
|
||||
: public IntrusiveListHook
|
||||
: public IntrusiveListHook<>
|
||||
{
|
||||
public:
|
||||
typedef T *pointer;
|
||||
|
|
|
@ -79,7 +79,7 @@ class UPnPDeviceDirectory final : UpnpCallback {
|
|||
};
|
||||
|
||||
class Downloader final
|
||||
: public IntrusiveListHook, CurlResponseHandler
|
||||
: public IntrusiveListHook<>, CurlResponseHandler
|
||||
{
|
||||
InjectEvent defer_start_event;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class HttpdOutput;
|
|||
|
||||
class HttpdClient final
|
||||
: BufferedSocket,
|
||||
public IntrusiveListHook
|
||||
public IntrusiveListHook<>
|
||||
{
|
||||
/**
|
||||
* The httpd output object this client is connected to.
|
||||
|
|
|
@ -33,7 +33,7 @@ struct SnapcastTime;
|
|||
class SnapcastOutput;
|
||||
class UniqueSocketDescriptor;
|
||||
|
||||
class SnapcastClient final : BufferedSocket, public IntrusiveListHook
|
||||
class SnapcastClient final : BufferedSocket, public IntrusiveListHook<>
|
||||
{
|
||||
SnapcastOutput &output;
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2022 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Specifies the mode in which a hook for intrusive containers
|
||||
* operates. This is meant to be used as a template argument to the
|
||||
* hook class (e.g. #IntrusiveListHook).
|
||||
*/
|
||||
enum class IntrusiveHookMode {
|
||||
/**
|
||||
* No implicit initialization.
|
||||
*/
|
||||
NORMAL,
|
||||
|
||||
/**
|
||||
* Keep track of whether the item is currently linked, allows
|
||||
* using method is_linked(). This requires implicit
|
||||
* initialization and requires iterating all items when
|
||||
* deleting them which adds a considerable amount of overhead.
|
||||
*/
|
||||
TRACK,
|
||||
|
||||
/**
|
||||
* Automatically unlinks the item in the destructor. This
|
||||
* implies #TRACK and adds code to the destructor.
|
||||
*/
|
||||
AUTO_UNLINK,
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -36,17 +36,17 @@
|
|||
|
||||
namespace {
|
||||
|
||||
template<typename Hook>
|
||||
struct CharItem final : Hook {
|
||||
template<IntrusiveHookMode mode>
|
||||
struct CharItem final : IntrusiveListHook<mode> {
|
||||
char ch;
|
||||
|
||||
constexpr CharItem(char _ch) noexcept:ch(_ch) {}
|
||||
};
|
||||
|
||||
template<typename Hook>
|
||||
template<IntrusiveHookMode mode>
|
||||
static std::string
|
||||
ToString(const IntrusiveList<CharItem<Hook>> &list,
|
||||
typename IntrusiveList<CharItem<Hook>>::const_iterator it,
|
||||
ToString(const IntrusiveList<CharItem<mode>> &list,
|
||||
typename IntrusiveList<CharItem<mode>>::const_iterator it,
|
||||
std::size_t n) noexcept
|
||||
{
|
||||
std::string result;
|
||||
|
@ -55,10 +55,10 @@ ToString(const IntrusiveList<CharItem<Hook>> &list,
|
|||
return result;
|
||||
}
|
||||
|
||||
template<typename Hook>
|
||||
template<IntrusiveHookMode mode>
|
||||
static std::string
|
||||
ToStringReverse(const IntrusiveList<CharItem<Hook>> &list,
|
||||
typename IntrusiveList<CharItem<Hook>>::const_iterator it,
|
||||
ToStringReverse(const IntrusiveList<CharItem<mode>> &list,
|
||||
typename IntrusiveList<CharItem<mode>>::const_iterator it,
|
||||
std::size_t n) noexcept
|
||||
{
|
||||
std::string result;
|
||||
|
@ -71,7 +71,7 @@ ToStringReverse(const IntrusiveList<CharItem<Hook>> &list,
|
|||
|
||||
TEST(IntrusiveList, Basic)
|
||||
{
|
||||
using Item = CharItem<IntrusiveListHook>;
|
||||
using Item = CharItem<IntrusiveHookMode::NORMAL>;
|
||||
|
||||
Item items[]{'a', 'b', 'c'};
|
||||
|
||||
|
@ -103,9 +103,9 @@ TEST(IntrusiveList, Basic)
|
|||
ASSERT_EQ(ToStringReverse(list, list.begin(), 6), "a_cfea");
|
||||
}
|
||||
|
||||
TEST(IntrusiveList, SafeLink)
|
||||
TEST(IntrusiveList, Track)
|
||||
{
|
||||
using Item = CharItem<SafeLinkIntrusiveListHook>;
|
||||
using Item = CharItem<IntrusiveHookMode::TRACK>;
|
||||
|
||||
Item items[]{'a', 'b', 'c'};
|
||||
|
||||
|
@ -162,7 +162,7 @@ TEST(IntrusiveList, SafeLink)
|
|||
|
||||
TEST(IntrusiveList, AutoUnlink)
|
||||
{
|
||||
using Item = CharItem<AutoUnlinkIntrusiveListHook>;
|
||||
using Item = CharItem<IntrusiveHookMode::AUTO_UNLINK>;
|
||||
|
||||
Item a{'a'};
|
||||
ASSERT_FALSE(a.is_linked());
|
||||
|
@ -194,7 +194,7 @@ TEST(IntrusiveList, AutoUnlink)
|
|||
|
||||
TEST(IntrusiveList, Merge)
|
||||
{
|
||||
using Item = CharItem<IntrusiveListHook>;
|
||||
using Item = CharItem<IntrusiveHookMode::NORMAL>;
|
||||
|
||||
const auto predicate = [](const Item &a, const Item &b){
|
||||
return a.ch < b.ch;
|
||||
|
@ -229,7 +229,7 @@ TEST(IntrusiveList, Merge)
|
|||
|
||||
TEST(IntrusiveList, Sort)
|
||||
{
|
||||
using Item = CharItem<IntrusiveListHook>;
|
||||
using Item = CharItem<IntrusiveHookMode::NORMAL>;
|
||||
|
||||
const auto predicate = [](const Item &a, const Item &b){
|
||||
return a.ch < b.ch;
|
||||
|
|
Loading…
Reference in New Issue