From dcd7c6337c13a8e4d0742765f335da0373ec2c86 Mon Sep 17 00:00:00 2001
From: Max Kellermann <mk@cm4all.com>
Date: Wed, 2 Aug 2023 23:25:50 +0200
Subject: [PATCH] util/Intrusive{List,HashSet}: reimplement hook mode detection
 with implicit cast

---
 src/util/IntrusiveHashSet.hxx | 31 ++++++++++++++-----------------
 src/util/IntrusiveList.hxx    | 33 ++++++++++++++-------------------
 2 files changed, 28 insertions(+), 36 deletions(-)

diff --git a/src/util/IntrusiveHashSet.hxx b/src/util/IntrusiveHashSet.hxx
index fb949d642..4f4d2f5fb 100644
--- a/src/util/IntrusiveHashSet.hxx
+++ b/src/util/IntrusiveHashSet.hxx
@@ -25,29 +25,26 @@ struct IntrusiveHashSetHook {
 	}
 };
 
-/**
- * Detect the hook type.
- */
-template<typename U>
-struct IntrusiveHashSetHookDetection {
-	/* TODO can this be simplified somehow, without checking for
-	   all possible enum values? */
-	using type = std::conditional_t<std::is_base_of_v<IntrusiveHashSetHook<IntrusiveHookMode::NORMAL>, U>,
-					IntrusiveHashSetHook<IntrusiveHookMode::NORMAL>,
-					std::conditional_t<std::is_base_of_v<IntrusiveHashSetHook<IntrusiveHookMode::TRACK>, U>,
-							   IntrusiveHashSetHook<IntrusiveHookMode::TRACK>,
-							   std::conditional_t<std::is_base_of_v<IntrusiveHashSetHook<IntrusiveHookMode::AUTO_UNLINK>, U>,
-									      IntrusiveHashSetHook<IntrusiveHookMode::AUTO_UNLINK>,
-									      void>>>;
-};
-
 /**
  * For classes which embed #IntrusiveHashSetHook as base class.
  */
 template<typename T>
 struct IntrusiveHashSetBaseHookTraits {
+	/* a never-called helper function which is used by _Cast() */
+	template<IntrusiveHookMode mode>
+	static constexpr IntrusiveHashSetHook<mode> _Identity(const IntrusiveHashSetHook<mode> &) noexcept;
+
+	/* another never-called helper function which "calls"
+	   _Identity(), implicitly casting the item to the
+	   IntrusiveHashSetHook specialization; we use this to detect
+	   which IntrusiveHashSetHook specialization is used */
 	template<typename U>
-	using Hook = typename IntrusiveHashSetHookDetection<U>::type;
+	static constexpr auto _Cast(const U &u) noexcept {
+		return decltype(_Identity(u))();
+	}
+
+	template<typename U>
+	using Hook = decltype(_Cast(std::declval<U>()));
 
 	static constexpr T *Cast(Hook<T> *node) noexcept {
 		return static_cast<T *>(node);
diff --git a/src/util/IntrusiveList.hxx b/src/util/IntrusiveList.hxx
index 27e5975d9..64ba6aaaf 100644
--- a/src/util/IntrusiveList.hxx
+++ b/src/util/IntrusiveList.hxx
@@ -77,31 +77,26 @@ using SafeLinkIntrusiveListHook =
 using AutoUnlinkIntrusiveListHook =
 	IntrusiveListHook<IntrusiveHookMode::AUTO_UNLINK>;
 
-/**
- * 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 {
-	/* 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>>>;
-};
-
 /**
  * For classes which embed #IntrusiveListHook as base class.
  */
 template<typename T>
 struct IntrusiveListBaseHookTraits {
+	/* a never-called helper function which is used by _Cast() */
+	template<IntrusiveHookMode mode>
+	static constexpr IntrusiveListHook<mode> _Identity(const IntrusiveListHook<mode> &) noexcept;
+
+	/* another never-called helper function which "calls"
+	   _Identity(), implicitly casting the item to the
+	   IntrusiveListHook specialization; we use this to detect
+	   which IntrusiveListHook specialization is used */
 	template<typename U>
-	using Hook = typename IntrusiveListHookDetection<U>::type;
+	static constexpr auto _Cast(const U &u) noexcept {
+		return decltype(_Identity(u))();
+	}
+
+	template<typename U>
+	using Hook = decltype(_Cast(std::declval<U>()));
 
 	static constexpr T *Cast(IntrusiveListNode *node) noexcept {
 		auto *hook = &Hook<T>::Cast(*node);