Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-13 07:57:38

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
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 /// Ownership enum for @c Delegate
0020 enum class DelegateType { Owning, NonOwning };
0021 
0022 /// Tag type to select a compile-time callable for Delegate.
0023 /// @tparam C Callable value used for binding.
0024 template <auto C>
0025 struct DelegateFuncTag {
0026   explicit constexpr DelegateFuncTag() = default;
0027 };
0028 
0029 // Specialization needed for defaulting ownership and for R(Args...) syntax
0030 template <typename, typename H = void, DelegateType = DelegateType::NonOwning>
0031 class Delegate;
0032 
0033 /// Delegate type that allows type erasure of a callable without allocation
0034 /// and with a single level of indirection. This type can support:
0035 /// - a free function pointer
0036 /// - a pointer to a member function alongside an instance pointer
0037 /// @note @c Delegate by default does not assume ownership of the instance.
0038 ///          In that case You need to ensure that the lifetime of the callable
0039 ///          instance is longer than that of the @c Delegate. If you set @c O
0040 ///          to @c DelegateType::Owning, it will assume ownership.
0041 /// @note Currently @c Delegate only supports callables that are ``const``
0042 /// @tparam R Return type of the function signature
0043 /// @tparam H Holder type that is used to store an instance
0044 /// @tparam O Ownership type of the delegate: Owning or NonOwning
0045 /// @tparam Args Types of the arguments of the function signatures
0046 ///
0047 template <typename R, typename H, DelegateType O, typename... Args>
0048 class Delegate<R(Args...), H, O> {
0049  public:
0050   /// Delegate ownership type indicating resource management strategy
0051   static constexpr DelegateType kOwnership = O;
0052 
0053   /// Alias of the return type
0054   using return_type = R;
0055   /// Type alias for holder type used to store instance pointer
0056   using holder_type = H;
0057   /// Type alias for function pointer type with holder parameter
0058   using function_type = return_type (*)(const holder_type *, Args...);
0059   /// Type alias for plain function pointer type without holder
0060   using function_ptr_type = return_type (*)(Args...);
0061   /// Type alias for function signature
0062   using signature_type = R(Args...);
0063 
0064   /// Type alias for deleter function pointer used for cleanup
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   /// Move constructor
0087   Delegate(Delegate &&) noexcept = default;
0088   /// Move assignment operator
0089   /// @return Reference to this Delegate after moving
0090   Delegate &operator=(Delegate &&) noexcept = default;
0091   /// Copy constructor
0092   Delegate(const Delegate &) noexcept = default;
0093   /// Copy assignment operator
0094   /// @return Reference to this Delegate after copying
0095   Delegate &operator=(const Delegate &) noexcept = default;
0096 
0097   /// @cond
0098   /// Constructor with an explicit runtime callable
0099   /// @param callable The runtime value of the callable
0100   /// @note The function signature requires the first argument of the callable is `const void*`.
0101   ///       i.e. if the signature of the delegate is `void(int)`, the
0102   ///       callable's signature has to be `void(const void*, int)`.
0103   explicit Delegate(function_type callable) { connect(callable); }
0104   /// @endcond
0105 
0106   /// Constructor with a possibly stateful function object.
0107   /// @tparam Callable Type of the callable
0108   /// @param callable The callable (function object or lambda)
0109   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0110   ///       it's lifetime is longer than that of @c Delegate.
0111   template <typename Callable>
0112   explicit Delegate(Callable &callable)
0113     requires(isNoFunPtr<Callable>::value)
0114   {
0115     connect(callable);
0116   }
0117 
0118   /// Constructor with a compile-time free function pointer
0119   /// @tparam Callable The compile-time free function pointer
0120   /// @note @c DelegateFuncTag is used to communicate the callable type
0121   template <auto Callable>
0122   explicit Delegate(DelegateFuncTag<Callable> /*tag*/) {
0123     connect<Callable>();
0124   }
0125 
0126   /// Constructor with a compile-time member function pointer and instance
0127   /// @tparam Callable The compile-time member function pointer
0128   /// @tparam Type The type of the instance the member function should be called on
0129   /// @param instance The instance on which the member function pointer should be called on
0130   /// @note @c Delegate does not assume owner ship over @p instance.
0131   /// @note @c DelegateFuncTag is used to communicate the callable type
0132   template <auto Callable, typename Type>
0133 
0134   Delegate(DelegateFuncTag<Callable> /*tag*/, const Type *instance)
0135     requires(kOwnership == DelegateType::NonOwning)
0136   {
0137     connect<Callable>(instance);
0138   }
0139 
0140   /// Constructor from rvalue reference is deleted, should catch construction
0141   /// with temporary objects and thus invalid pointers
0142   template <typename Callable>
0143   Delegate(Callable &&)
0144     requires(isNoFunPtr<Callable>::value)
0145   = delete;
0146 
0147   /// @cond
0148   /// Assignment operator with an explicit runtime callable
0149   /// @param callable The runtime value of the callable
0150   /// @note The function signature requires the first argument of the callable is `const void*`.
0151   ///       i.e. if the signature of the delegate is `void(int)`, the
0152   ///       callable's signature has to be `void(const void*, int)`.
0153   void operator=(function_type callable) { connect(callable); }
0154 
0155   /// Assignment operator with possibly stateful function object.
0156   /// @tparam Callable Type of the callable
0157   /// @param callable The callable (function object or lambda)
0158   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0159   ///       it's lifetime is longer than that of @c Delegate.
0160   template <typename Callable>
0161   void operator=(Callable &callable)
0162     requires(isNoFunPtr<Callable>::value)
0163   {
0164     connect(callable);
0165   }
0166   /// @endcond
0167 
0168   /// Assignment operator from rvalue reference is deleted, should catch
0169   /// assignment from temporary objects and thus invalid pointers
0170   template <typename Callable>
0171   void operator=(Callable &&)
0172     requires(isNoFunPtr<Callable>::value)
0173   = delete;
0174 
0175   /// @cond
0176   /// Connect a free function pointer.
0177   /// @note The function pointer must be ``constexpr`` for @c Delegate to accept it
0178   /// @tparam Callable The compile-time free function pointer
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 * /*payload*/,
0192                     Args... args) -> return_type {
0193       return std::invoke(Callable, std::forward<Args>(args)...);
0194     };
0195   }
0196   /// @endcond
0197 
0198   /// Assignment operator with possibly stateful function object.
0199   /// @tparam Callable Type of the callable
0200   /// @param callable The callable (function object or lambda)
0201   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0202   ///       it's lifetime is longer than that of @c Delegate.
0203   template <typename Callable>
0204   void connect(Callable &callable)
0205     requires(isNoFunPtr<Callable>::value)
0206   {
0207     connect<&Callable::operator(), Callable>(&callable);
0208   }
0209 
0210   /// Connection with rvalue reference is deleted, should catch assignment
0211   /// from temporary objects and thus invalid pointers
0212   template <typename Callable>
0213   void connect(Callable &&)
0214     requires(isNoFunPtr<Callable>::value)
0215   = delete;
0216 
0217   /// @cond
0218   /// Connect anything that is assignable to the function pointer
0219   /// @param callable The runtime value of the callable
0220   /// @note The function signature requires the first argument of the callable is `const void*`.
0221   ///       i.e. if the signature of the delegate is `void(int)`, the
0222   ///       callable's signature has to be `void(const void*, int)`.
0223   void connect(function_type callable) {
0224     if constexpr (kOwnership == DelegateType::NonOwning) {
0225       m_payload.payload = nullptr;
0226     }
0227     m_function = callable;
0228   }
0229   /// @endcond
0230 
0231   /// Connect a runtime callable with an instance pointer
0232   /// @tparam Type The type of the instance
0233   /// @param callable The runtime callable function
0234   /// @param instance The instance pointer to be passed as first argument
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   /// Connect a member function to be called on an instance
0244   /// @tparam Callable The compile-time member function pointer
0245   /// @tparam Type The type of the instance the member function should be called on
0246   /// @param instance The instance on which the member function pointer should be called on
0247   /// @note @c Delegate does not assume owner ship over @p instance. You need to ensure
0248   ///       it's lifetime is longer than that of @c Delegate.
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   /// Connect a member function to be called on an instance
0267   /// @tparam Callable The compile-time member function pointer
0268   /// @tparam Type The type of the instance the member function should be called on
0269   /// @param instance The instance on which the member function pointer should be called on
0270   /// @note @c Delegate assumes owner ship over @p instance.
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   /// The call operator that exposes the functionality of the @c Delegate type.
0292   /// @param args The arguments to call the contained function with
0293   /// @return Return value of the contained function
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   /// Return whether this delegate is currently connected
0303   /// @return True if this delegate is connected
0304   bool connected() const { return m_function != nullptr; }
0305 
0306   /// Return whether this delegate is currently connected
0307   /// @return True if this delegate is connected
0308   explicit operator bool() const { return connected(); }
0309 
0310   /// Disconnect this delegate, meaning it cannot be called anymore
0311   void disconnect() {
0312     m_payload.clear();
0313     m_function = nullptr;
0314   }
0315 
0316   /// Get the stored instance pointer
0317   /// @return Pointer to the stored instance, or nullptr if none
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   // Deleter that does not do anything
0326   static void noopDeleter(const holder_type * /*unused*/) {
0327     // we do not own the payload
0328   }
0329 
0330   /// @cond
0331 
0332   // Payload object without a deleter
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   // Payload object with a deleter
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   /// Stores the instance pointer and maybe a deleter
0352   std::conditional_t<kOwnership == DelegateType::NonOwning, NonOwningPayload,
0353                      OwningPayload>
0354       m_payload;
0355 
0356   /// @endcond
0357 
0358   /// Stores the function pointer wrapping the compile time function pointer given in @c connect().
0359   function_type m_function{nullptr};
0360 };
0361 
0362 template <typename, typename H = void>
0363 class OwningDelegate;
0364 
0365 /// Alias for an owning delegate
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   /// Construct OwningDelegate from owning Delegate
0372   /// @param delegate The owning delegate to move from
0373   explicit OwningDelegate(
0374       Delegate<R(Args...), H, DelegateType::Owning> &&delegate)
0375       : Delegate<R(Args...), H, DelegateType::Owning>(std::move(delegate)) {}
0376 
0377   /// Move assignment from owning Delegate
0378   /// @param delegate The owning delegate to move from
0379   /// @return Reference to this OwningDelegate
0380   OwningDelegate &operator=(
0381       Delegate<R(Args...), H, DelegateType::Owning> &&delegate) {
0382     *this = OwningDelegate{std::move(delegate)};
0383     return *this;
0384   }
0385 };
0386 
0387 }  // namespace Acts