File indexing completed on 2025-08-06 08:10:16
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Utilities/TypeTraits.hpp"
0012
0013 #include <cassert>
0014 #include <functional>
0015 #include <memory>
0016 #include <type_traits>
0017
0018 namespace Acts {
0019
0020
0021 enum class DelegateType { Owning, NonOwning };
0022
0023 template <auto C>
0024 struct DelegateFuncTag {
0025 explicit constexpr DelegateFuncTag() = default;
0026 };
0027
0028
0029 template <typename, typename H = void, DelegateType = DelegateType::NonOwning>
0030 class Delegate;
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046 template <typename R, typename H, DelegateType O, typename... Args>
0047 class Delegate<R(Args...), H, O> {
0048 static constexpr DelegateType kOwnership = O;
0049
0050
0051 using return_type = R;
0052 using holder_type = H;
0053
0054 using function_type = return_type (*)(const holder_type *, Args...);
0055
0056 using function_ptr_type = return_type (*)(Args...);
0057
0058 using deleter_type = void (*)(const holder_type *);
0059
0060 template <typename T, typename C>
0061 using isSignatureCompatible =
0062 decltype(std::declval<T &>() = std::declval<C>());
0063
0064 using OwningDelegate =
0065 Delegate<R(Args...), holder_type, DelegateType::Owning>;
0066 using NonOwningDelegate =
0067 Delegate<R(Args...), holder_type, DelegateType::NonOwning>;
0068 template <typename T>
0069 using isNoFunPtr =
0070 std::enable_if_t<!std::is_convertible_v<std::decay_t<T>, function_type> &&
0071 !std::is_same_v<std::decay_t<T>, OwningDelegate> &&
0072 !std::is_same_v<std::decay_t<T>, NonOwningDelegate>>;
0073
0074 public:
0075 Delegate() = default;
0076
0077 Delegate(Delegate &&) = default;
0078 Delegate &operator=(Delegate &&) = default;
0079 Delegate(const Delegate &) = default;
0080 Delegate &operator=(const Delegate &) = default;
0081
0082
0083
0084
0085
0086
0087 Delegate(function_type callable) { connect(callable); }
0088
0089
0090
0091
0092
0093
0094 template <typename Callable, typename = isNoFunPtr<Callable>>
0095 Delegate(Callable &callable) {
0096 connect(callable);
0097 }
0098
0099
0100
0101
0102 template <auto Callable>
0103 Delegate(DelegateFuncTag<Callable> ) {
0104 connect<Callable>();
0105 }
0106
0107
0108
0109
0110
0111
0112
0113 template <auto Callable, typename Type, DelegateType T = kOwnership,
0114 typename = std::enable_if_t<T == DelegateType::NonOwning>>
0115 Delegate(DelegateFuncTag<Callable> , const Type *instance) {
0116 connect<Callable>(instance);
0117 }
0118
0119
0120
0121 template <typename Callable, typename = isNoFunPtr<Callable>>
0122 Delegate(Callable &&) = delete;
0123
0124
0125
0126
0127
0128
0129 void operator=(function_type callable) { connect(callable); }
0130
0131
0132
0133
0134
0135
0136 template <typename Callable, typename = isNoFunPtr<Callable>>
0137 void operator=(Callable &callable) {
0138 connect(callable);
0139 }
0140
0141
0142
0143 template <typename Callable, typename = isNoFunPtr<Callable>>
0144 void operator=(Callable &&) = delete;
0145
0146
0147
0148
0149 template <auto Callable>
0150 void connect() {
0151 m_payload.payload = nullptr;
0152
0153 static_assert(
0154 Concepts::is_detected<isSignatureCompatible, function_ptr_type,
0155 decltype(Callable)>::value,
0156 "Callable given does not correspond exactly to required call "
0157 "signature");
0158
0159 m_function = [](const holder_type * ,
0160 Args... args) -> return_type {
0161 return std::invoke(Callable, std::forward<Args>(args)...);
0162 };
0163 }
0164
0165
0166
0167
0168
0169
0170 template <typename Callable, typename = isNoFunPtr<Callable>>
0171 void connect(Callable &callable) {
0172 connect<&Callable::operator(), Callable>(&callable);
0173 }
0174
0175
0176
0177 template <typename Callable, typename = isNoFunPtr<Callable>>
0178 void connect(Callable &&) = delete;
0179
0180
0181
0182
0183
0184
0185 void connect(function_type callable) {
0186 if constexpr (kOwnership == DelegateType::NonOwning) {
0187 m_payload.payload = nullptr;
0188 }
0189 m_function = callable;
0190 }
0191
0192
0193
0194
0195
0196
0197
0198 template <auto Callable, typename Type, DelegateType T = kOwnership,
0199 typename = std::enable_if_t<T == DelegateType::NonOwning>>
0200 void connect(const Type *instance) {
0201 using member_ptr_type = return_type (Type::*)(Args...) const;
0202
0203 static_assert(Concepts::is_detected<isSignatureCompatible, member_ptr_type,
0204 decltype(Callable)>::value,
0205 "Callable given does not correspond exactly to required call "
0206 "signature");
0207
0208 m_payload.payload = instance;
0209
0210 m_function = [](const holder_type *payload, Args... args) -> return_type {
0211 assert(payload != nullptr && "Payload is required, but not set");
0212 const auto *concretePayload = static_cast<const Type *>(payload);
0213 return std::invoke(Callable, concretePayload,
0214 std::forward<Args>(args)...);
0215 };
0216 }
0217
0218
0219
0220
0221
0222
0223 template <auto Callable, typename Type, DelegateType T = kOwnership,
0224 typename = std::enable_if_t<T == DelegateType::Owning>>
0225 void connect(std::unique_ptr<const Type> instance) {
0226 using member_ptr_type = return_type (Type::*)(Args...) const;
0227 static_assert(Concepts::is_detected<isSignatureCompatible, member_ptr_type,
0228 decltype(Callable)>::value,
0229 "Callable given does not correspond exactly to required call "
0230 "signature");
0231
0232 m_payload.payload = std::unique_ptr<const holder_type, deleter_type>(
0233 instance.release(), [](const holder_type *payload) {
0234 const auto *concretePayload = static_cast<const Type *>(payload);
0235 delete concretePayload;
0236 });
0237
0238 m_function = [](const holder_type *payload, Args... args) -> return_type {
0239 assert(payload != nullptr && "Payload is required, but not set");
0240 const auto *concretePayload = static_cast<const Type *>(payload);
0241 return std::invoke(Callable, concretePayload,
0242 std::forward<Args>(args)...);
0243 };
0244 }
0245
0246
0247
0248
0249 return_type operator()(Args... args) const {
0250 assert(connected() && "Delegate is not connected");
0251 return std::invoke(m_function, m_payload.ptr(),
0252 std::forward<Args>(args)...);
0253 }
0254
0255
0256
0257 bool connected() const { return m_function != nullptr; }
0258
0259
0260
0261 operator bool() const { return connected(); }
0262
0263
0264 void disconnect() {
0265 m_payload.clear();
0266 m_function = nullptr;
0267 }
0268
0269 template <typename holder_t = holder_type,
0270 typename = std::enable_if_t<!std::is_same_v<holder_t, void>>>
0271 const holder_type *instance() const {
0272 return m_payload.ptr();
0273 }
0274
0275 private:
0276
0277 static void noopDeleter(const holder_type * ) {}
0278
0279
0280
0281
0282 struct NonOwningPayload {
0283 void clear() { payload = nullptr; }
0284
0285 const holder_type *ptr() const { return payload; }
0286
0287 const holder_type *payload{nullptr};
0288 };
0289
0290
0291 struct OwningPayload {
0292 void clear() { payload.reset(); }
0293
0294 const holder_type *ptr() const { return payload.get(); }
0295
0296 std::unique_ptr<const holder_type, deleter_type> payload{nullptr,
0297 &noopDeleter};
0298 };
0299
0300
0301 std::conditional_t<kOwnership == DelegateType::NonOwning, NonOwningPayload,
0302 OwningPayload>
0303 m_payload;
0304
0305
0306
0307
0308 function_type m_function{nullptr};
0309 };
0310
0311 template <typename, typename H = void>
0312 class OwningDelegate;
0313
0314
0315 template <typename R, typename H, typename... Args>
0316 class OwningDelegate<R(Args...), H>
0317 : public Delegate<R(Args...), H, DelegateType::Owning> {};
0318
0319 }