Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-14 08:00:12

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