Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 09:25:39

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(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 * /*payload*/,
0191                     Args... args) -> return_type {
0192       return std::invoke(Callable, std::forward<Args>(args)...);
0193     };
0194   }
0195   /// @endcond
0196 
0197   /// Assignment operator with possibly stateful function object.
0198   /// @tparam Callable Type of the callable
0199   /// @param callable The callable (function object or lambda)
0200   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0201   ///       it's lifetime is longer than that of @c Delegate.
0202   template <typename Callable>
0203   void connect(Callable &callable)
0204     requires(isNoFunPtr<Callable>::value)
0205   {
0206     connect<&Callable::operator(), Callable>(&callable);
0207   }
0208 
0209   /// Connection with rvalue reference is deleted, should catch assignment
0210   /// from temporary objects and thus invalid pointers
0211   template <typename Callable>
0212   void connect(Callable &&)
0213     requires(isNoFunPtr<Callable>::value)
0214   = delete;
0215 
0216   /// @cond
0217   /// Connect anything that is assignable to the function pointer
0218   /// @param callable The runtime value of the callable
0219   /// @note The function signature requires the first argument of the callable is `const void*`.
0220   ///       i.e. if the signature of the delegate is `void(int)`, the
0221   ///       callable's signature has to be `void(const void*, int)`.
0222   void connect(function_type callable) {
0223     if constexpr (kOwnership == DelegateType::NonOwning) {
0224       m_payload.payload = nullptr;
0225     }
0226     m_function = callable;
0227   }
0228   /// @endcond
0229 
0230   /// Connect a runtime callable with an instance pointer
0231   /// @tparam Type The type of the instance
0232   /// @param callable The runtime callable function
0233   /// @param instance The instance pointer to be passed as first argument
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   /// Connect a member function to be called on an instance
0243   /// @tparam Callable The compile-time member function pointer
0244   /// @tparam Type The type of the instance the member function should be called on
0245   /// @param instance The instance on which the member function pointer should be called on
0246   /// @note @c Delegate does not assume owner ship over @p instance. You need to ensure
0247   ///       it's lifetime is longer than that of @c Delegate.
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   /// Connect a member function to be called on an instance
0266   /// @tparam Callable The compile-time member function pointer
0267   /// @tparam Type The type of the instance the member function should be called on
0268   /// @param instance The instance on which the member function pointer should be called on
0269   /// @note @c Delegate assumes owner ship over @p instance.
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   /// The call operator that exposes the functionality of the @c Delegate type.
0291   /// @param args The arguments to call the contained function with
0292   /// @return Return value of the contained function
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   /// Return whether this delegate is currently connected
0302   /// @return True if this delegate is connected
0303   bool connected() const { return m_function != nullptr; }
0304 
0305   /// Return whether this delegate is currently connected
0306   /// @return True if this delegate is connected
0307   explicit operator bool() const { return connected(); }
0308 
0309   /// Disconnect this delegate, meaning it cannot be called anymore
0310   void disconnect() {
0311     m_payload.clear();
0312     m_function = nullptr;
0313   }
0314 
0315   /// Get the stored instance pointer
0316   /// @return Pointer to the stored instance, or nullptr if none
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   // Deleter that does not do anything
0325   static void noopDeleter(const holder_type * /*unused*/) {
0326     // we do not own the payload
0327   }
0328 
0329   /// @cond
0330 
0331   // Payload object without a deleter
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   // Payload object with a deleter
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   /// Stores the instance pointer and maybe a deleter
0351   std::conditional_t<kOwnership == DelegateType::NonOwning, NonOwningPayload,
0352                      OwningPayload>
0353       m_payload;
0354 
0355   /// @endcond
0356 
0357   /// Stores the function pointer wrapping the compile time function pointer given in @c connect().
0358   function_type m_function{nullptr};
0359 };
0360 
0361 template <typename, typename H = void>
0362 class OwningDelegate;
0363 
0364 /// Alias for an owning delegate
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   /// Construct OwningDelegate from owning Delegate
0371   /// @param delegate The owning delegate to move from
0372   explicit OwningDelegate(
0373       Delegate<R(Args...), H, DelegateType::Owning> &&delegate)
0374       : Delegate<R(Args...), H, DelegateType::Owning>(std::move(delegate)) {}
0375 
0376   /// Move assignment from owning Delegate
0377   /// @param delegate The owning delegate to move from
0378   /// @return Reference to this OwningDelegate
0379   OwningDelegate &operator=(
0380       Delegate<R(Args...), H, DelegateType::Owning> &&delegate) {
0381     *this = OwningDelegate{std::move(delegate)};
0382     return *this;
0383   }
0384 };
0385 
0386 }  // namespace Acts