File indexing completed on 2026-05-13 07:57:38
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(
0182 Concepts::invocable_and_returns<Callable, return_type, Args && ...>)
0183 {
0184 m_payload.payload = nullptr;
0185
0186 static_assert(
0187 Concepts::invocable_and_returns<Callable, return_type, Args &&...>,
0188 "Callable given does not correspond exactly to required call "
0189 "signature");
0190
0191 m_function = [](const holder_type * ,
0192 Args... args) -> return_type {
0193 return std::invoke(Callable, std::forward<Args>(args)...);
0194 };
0195 }
0196
0197
0198
0199
0200
0201
0202
0203 template <typename Callable>
0204 void connect(Callable &callable)
0205 requires(isNoFunPtr<Callable>::value)
0206 {
0207 connect<&Callable::operator(), Callable>(&callable);
0208 }
0209
0210
0211
0212 template <typename Callable>
0213 void connect(Callable &&)
0214 requires(isNoFunPtr<Callable>::value)
0215 = delete;
0216
0217
0218
0219
0220
0221
0222
0223 void connect(function_type callable) {
0224 if constexpr (kOwnership == DelegateType::NonOwning) {
0225 m_payload.payload = nullptr;
0226 }
0227 m_function = callable;
0228 }
0229
0230
0231
0232
0233
0234
0235 template <typename Type>
0236 void connect(function_type callable, const Type *instance)
0237 requires(kOwnership == DelegateType::NonOwning)
0238 {
0239 m_payload.payload = instance;
0240 m_function = callable;
0241 }
0242
0243
0244
0245
0246
0247
0248
0249 template <auto Callable, typename Type>
0250 void connect(const Type *instance)
0251 requires(kOwnership == DelegateType::NonOwning &&
0252 Concepts::invocable_and_returns<Callable, return_type, Type,
0253 Args && ...>)
0254
0255 {
0256 m_payload.payload = instance;
0257
0258 m_function = [](const holder_type *payload, Args... args) -> return_type {
0259 assert(payload != nullptr && "Payload is required, but not set");
0260 const auto *concretePayload = static_cast<const Type *>(payload);
0261 return std::invoke(Callable, concretePayload,
0262 std::forward<Args>(args)...);
0263 };
0264 }
0265
0266
0267
0268
0269
0270
0271 template <auto Callable, typename Type>
0272 void connect(std::unique_ptr<const Type> instance)
0273 requires(kOwnership == DelegateType::Owning &&
0274 Concepts::invocable_and_returns<Callable, return_type, Type,
0275 Args && ...>)
0276 {
0277 m_payload.payload = std::unique_ptr<const holder_type, deleter_type>(
0278 instance.release(), [](const holder_type *payload) {
0279 const auto *concretePayload = static_cast<const Type *>(payload);
0280 delete concretePayload;
0281 });
0282
0283 m_function = [](const holder_type *payload, Args &&...args) -> return_type {
0284 assert(payload != nullptr && "Payload is required, but not set");
0285 const auto *concretePayload = static_cast<const Type *>(payload);
0286 return std::invoke(Callable, concretePayload,
0287 std::forward<Args>(args)...);
0288 };
0289 }
0290
0291
0292
0293
0294 template <typename... Ts>
0295 return_type operator()(Ts &&...args) const
0296 requires(std::is_invocable_v<function_type, const holder_type *, Ts...>)
0297 {
0298 assert(connected() && "Delegate is not connected");
0299 return std::invoke(m_function, m_payload.ptr(), std::forward<Ts>(args)...);
0300 }
0301
0302
0303
0304 bool connected() const { return m_function != nullptr; }
0305
0306
0307
0308 explicit operator bool() const { return connected(); }
0309
0310
0311 void disconnect() {
0312 m_payload.clear();
0313 m_function = nullptr;
0314 }
0315
0316
0317
0318 const holder_type *instance() const
0319 requires(!std::same_as<holder_type, void>)
0320 {
0321 return m_payload.ptr();
0322 }
0323
0324 private:
0325
0326 static void noopDeleter(const holder_type * ) {
0327
0328 }
0329
0330
0331
0332
0333 struct NonOwningPayload {
0334 void clear() { payload = nullptr; }
0335
0336 const holder_type *ptr() const { return payload; }
0337
0338 const holder_type *payload{nullptr};
0339 };
0340
0341
0342 struct OwningPayload {
0343 void clear() { payload.reset(); }
0344
0345 const holder_type *ptr() const { return payload.get(); }
0346
0347 std::unique_ptr<const holder_type, deleter_type> payload{nullptr,
0348 &noopDeleter};
0349 };
0350
0351
0352 std::conditional_t<kOwnership == DelegateType::NonOwning, NonOwningPayload,
0353 OwningPayload>
0354 m_payload;
0355
0356
0357
0358
0359 function_type m_function{nullptr};
0360 };
0361
0362 template <typename, typename H = void>
0363 class OwningDelegate;
0364
0365
0366 template <typename R, typename H, typename... Args>
0367 class OwningDelegate<R(Args...), H>
0368 : public Delegate<R(Args...), H, DelegateType::Owning> {
0369 public:
0370 OwningDelegate() = default;
0371
0372
0373 explicit OwningDelegate(
0374 Delegate<R(Args...), H, DelegateType::Owning> &&delegate)
0375 : Delegate<R(Args...), H, DelegateType::Owning>(std::move(delegate)) {}
0376
0377
0378
0379
0380 OwningDelegate &operator=(
0381 Delegate<R(Args...), H, DelegateType::Owning> &&delegate) {
0382 *this = OwningDelegate{std::move(delegate)};
0383 return *this;
0384 }
0385 };
0386
0387 }