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