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