Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:10:16

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2021 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 http://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include "Acts/Utilities/TypeTraits.hpp"
0012 
0013 #include <cassert>
0014 #include <functional>
0015 #include <memory>
0016 #include <type_traits>
0017 
0018 namespace Acts {
0019 
0020 /// Ownership enum for @c Delegate
0021 enum class DelegateType { Owning, NonOwning };
0022 
0023 template <auto C>
0024 struct DelegateFuncTag {
0025   explicit constexpr DelegateFuncTag() = default;
0026 };
0027 
0028 // Specialization needed for defaulting ownership and for R(Args...) syntax
0029 template <typename, typename H = void, DelegateType = DelegateType::NonOwning>
0030 class Delegate;
0031 
0032 /// Delegate type that allows type erasure of a callable without allocation
0033 /// and with a single level of indirection. This type can support:
0034 /// - a free function pointer
0035 /// - a pointer to a member function alongside an instance pointer
0036 /// @note @c Delegate by default does not assume ownership of the instance.
0037 ///          In that case You need to ensure that the lifetime of the callable
0038 ///          instance is longer than that of the @c Delegate. If you set @c O
0039 ///          to @c DelegateType::Owning, it will assume ownership.
0040 /// @note Currently @c Delegate only supports callables that are ``const``
0041 /// @tparam R Return type of the function signature
0042 /// @tparam H Holder type that is used to store an instance
0043 /// @tparam O Ownership type of the delegate: Owning or NonOwning
0044 /// @tparam Args Types of the arguments of the function signatures
0045 ///
0046 template <typename R, typename H, DelegateType O, typename... Args>
0047 class Delegate<R(Args...), H, O> {
0048   static constexpr DelegateType kOwnership = O;
0049 
0050   /// Alias of the return type
0051   using return_type = R;
0052   using holder_type = H;
0053   /// Alias to the function pointer type this class will store
0054   using function_type = return_type (*)(const holder_type *, Args...);
0055 
0056   using function_ptr_type = return_type (*)(Args...);
0057 
0058   using deleter_type = void (*)(const holder_type *);
0059 
0060   template <typename T, typename C>
0061   using isSignatureCompatible =
0062       decltype(std::declval<T &>() = std::declval<C>());
0063 
0064   using OwningDelegate =
0065       Delegate<R(Args...), holder_type, DelegateType::Owning>;
0066   using NonOwningDelegate =
0067       Delegate<R(Args...), holder_type, DelegateType::NonOwning>;
0068   template <typename T>
0069   using isNoFunPtr =
0070       std::enable_if_t<!std::is_convertible_v<std::decay_t<T>, function_type> &&
0071                        !std::is_same_v<std::decay_t<T>, OwningDelegate> &&
0072                        !std::is_same_v<std::decay_t<T>, NonOwningDelegate>>;
0073 
0074  public:
0075   Delegate() = default;
0076 
0077   Delegate(Delegate &&) = default;
0078   Delegate &operator=(Delegate &&) = default;
0079   Delegate(const Delegate &) = default;
0080   Delegate &operator=(const Delegate &) = default;
0081 
0082   /// Constructor with an explicit runtime callable
0083   /// @param callable The runtime value of the callable
0084   /// @note The function signature requires the first argument of the callable is `const void*`.
0085   ///       i.e. if the signature of the delegate is `void(int)`, the
0086   ///       callable's signature has to be `void(const void*, int)`.
0087   Delegate(function_type callable) { connect(callable); }
0088 
0089   /// Constructor with a possibly stateful function object.
0090   /// @tparam Callable Type of the callable
0091   /// @param callable The callable (function object or lambda)
0092   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0093   ///       it's lifetime is longer than that of @c Delegate.
0094   template <typename Callable, typename = isNoFunPtr<Callable>>
0095   Delegate(Callable &callable) {
0096     connect(callable);
0097   }
0098 
0099   /// Constructor with a compile-time free function pointer
0100   /// @tparam Callable The compile-time free function pointer
0101   /// @note @c DelegateFuncTag is used to communicate the callable type
0102   template <auto Callable>
0103   Delegate(DelegateFuncTag<Callable> /*tag*/) {
0104     connect<Callable>();
0105   }
0106 
0107   /// Constructor with a compile-time member function pointer and instance
0108   /// @tparam Callable The compile-time member function pointer
0109   /// @tparam Type The type of the instance the member function should be called on
0110   /// @param instance The instance on which the member function pointer should be called on
0111   /// @note @c Delegate does not assume owner ship over @p instance.
0112   /// @note @c DelegateFuncTag is used to communicate the callable type
0113   template <auto Callable, typename Type, DelegateType T = kOwnership,
0114             typename = std::enable_if_t<T == DelegateType::NonOwning>>
0115   Delegate(DelegateFuncTag<Callable> /*tag*/, const Type *instance) {
0116     connect<Callable>(instance);
0117   }
0118 
0119   /// Constructor from rvalue reference is deleted, should catch construction
0120   /// with temporary objects and thus invalid pointers
0121   template <typename Callable, typename = isNoFunPtr<Callable>>
0122   Delegate(Callable &&) = delete;
0123 
0124   /// Assignment operator with an explicit runtime callable
0125   /// @param callable The runtime value of the callable
0126   /// @note The function signature requires the first argument of the callable is `const void*`.
0127   ///       i.e. if the signature of the delegate is `void(int)`, the
0128   ///       callable's signature has to be `void(const void*, int)`.
0129   void operator=(function_type callable) { connect(callable); }
0130 
0131   /// Assignment operator with possibly stateful function object.
0132   /// @tparam Callable Type of the callable
0133   /// @param callable The callable (function object or lambda)
0134   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0135   ///       it's lifetime is longer than that of @c Delegate.
0136   template <typename Callable, typename = isNoFunPtr<Callable>>
0137   void operator=(Callable &callable) {
0138     connect(callable);
0139   }
0140 
0141   /// Assignment operator from rvalue reference is deleted, should catch
0142   /// assignment from temporary objects and thus invalid pointers
0143   template <typename Callable, typename = isNoFunPtr<Callable>>
0144   void operator=(Callable &&) = delete;
0145 
0146   /// Connect a free function pointer.
0147   /// @note The function pointer must be ``constexpr`` for @c Delegate to accept it
0148   /// @tparam Callable The compile-time free function pointer
0149   template <auto Callable>
0150   void connect() {
0151     m_payload.payload = nullptr;
0152 
0153     static_assert(
0154         Concepts::is_detected<isSignatureCompatible, function_ptr_type,
0155                               decltype(Callable)>::value,
0156         "Callable given does not correspond exactly to required call "
0157         "signature");
0158 
0159     m_function = [](const holder_type * /*payload*/,
0160                     Args... args) -> return_type {
0161       return std::invoke(Callable, std::forward<Args>(args)...);
0162     };
0163   }
0164 
0165   /// Assignment operator with possibly stateful function object.
0166   /// @tparam Callable Type of the callable
0167   /// @param callable The callable (function object or lambda)
0168   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0169   ///       it's lifetime is longer than that of @c Delegate.
0170   template <typename Callable, typename = isNoFunPtr<Callable>>
0171   void connect(Callable &callable) {
0172     connect<&Callable::operator(), Callable>(&callable);
0173   }
0174 
0175   /// Connection with rvalue reference is deleted, should catch assignment
0176   /// from temporary objects and thus invalid pointers
0177   template <typename Callable, typename = isNoFunPtr<Callable>>
0178   void connect(Callable &&) = delete;
0179 
0180   /// Connect anything that is assignable to the function pointer
0181   /// @param callable The runtime value of the callable
0182   /// @note The function signature requires the first argument of the callable is `const void*`.
0183   ///       i.e. if the signature of the delegate is `void(int)`, the
0184   ///       callable's signature has to be `void(const void*, int)`.
0185   void connect(function_type callable) {
0186     if constexpr (kOwnership == DelegateType::NonOwning) {
0187       m_payload.payload = nullptr;
0188     }
0189     m_function = callable;
0190   }
0191 
0192   /// Connect a member function to be called on an instance
0193   /// @tparam Callable The compile-time member function pointer
0194   /// @tparam Type The type of the instance the member function should be called on
0195   /// @param instance The instance on which the member function pointer should be called on
0196   /// @note @c Delegate does not assume owner ship over @p instance. You need to ensure
0197   ///       it's lifetime is longer than that of @c Delegate.
0198   template <auto Callable, typename Type, DelegateType T = kOwnership,
0199             typename = std::enable_if_t<T == DelegateType::NonOwning>>
0200   void connect(const Type *instance) {
0201     using member_ptr_type = return_type (Type::*)(Args...) const;
0202 
0203     static_assert(Concepts::is_detected<isSignatureCompatible, member_ptr_type,
0204                                         decltype(Callable)>::value,
0205                   "Callable given does not correspond exactly to required call "
0206                   "signature");
0207 
0208     m_payload.payload = instance;
0209 
0210     m_function = [](const holder_type *payload, Args... args) -> return_type {
0211       assert(payload != nullptr && "Payload is required, but not set");
0212       const auto *concretePayload = static_cast<const Type *>(payload);
0213       return std::invoke(Callable, concretePayload,
0214                          std::forward<Args>(args)...);
0215     };
0216   }
0217 
0218   /// Connect a member function to be called on an instance
0219   /// @tparam Callable The compile-time member function pointer
0220   /// @tparam Type The type of the instance the member function should be called on
0221   /// @param instance The instance on which the member function pointer should be called on
0222   /// @note @c Delegate assumes owner ship over @p instance.
0223   template <auto Callable, typename Type, DelegateType T = kOwnership,
0224             typename = std::enable_if_t<T == DelegateType::Owning>>
0225   void connect(std::unique_ptr<const Type> instance) {
0226     using member_ptr_type = return_type (Type::*)(Args...) const;
0227     static_assert(Concepts::is_detected<isSignatureCompatible, member_ptr_type,
0228                                         decltype(Callable)>::value,
0229                   "Callable given does not correspond exactly to required call "
0230                   "signature");
0231 
0232     m_payload.payload = std::unique_ptr<const holder_type, deleter_type>(
0233         instance.release(), [](const holder_type *payload) {
0234           const auto *concretePayload = static_cast<const Type *>(payload);
0235           delete concretePayload;
0236         });
0237 
0238     m_function = [](const holder_type *payload, Args... args) -> return_type {
0239       assert(payload != nullptr && "Payload is required, but not set");
0240       const auto *concretePayload = static_cast<const Type *>(payload);
0241       return std::invoke(Callable, concretePayload,
0242                          std::forward<Args>(args)...);
0243     };
0244   }
0245 
0246   /// The call operator that exposes the functionality of the @c Delegate type.
0247   /// @param args The arguments to call the contained function with
0248   /// @return Return value of the contained function
0249   return_type operator()(Args... args) const {
0250     assert(connected() && "Delegate is not connected");
0251     return std::invoke(m_function, m_payload.ptr(),
0252                        std::forward<Args>(args)...);
0253   }
0254 
0255   /// Return whether this delegate is currently connected
0256   /// @return True if this delegate is connected
0257   bool connected() const { return m_function != nullptr; }
0258 
0259   /// Return whether this delegate is currently connected
0260   /// @return True if this delegate is connected
0261   operator bool() const { return connected(); }
0262 
0263   /// Disconnect this delegate, meaning it cannot be called anymore
0264   void disconnect() {
0265     m_payload.clear();
0266     m_function = nullptr;
0267   }
0268 
0269   template <typename holder_t = holder_type,
0270             typename = std::enable_if_t<!std::is_same_v<holder_t, void>>>
0271   const holder_type *instance() const {
0272     return m_payload.ptr();
0273   }
0274 
0275  private:
0276   // Deleter that does not do anything
0277   static void noopDeleter(const holder_type * /*unused*/) {}
0278 
0279   /// @cond
0280 
0281   // Payload object without a deleter
0282   struct NonOwningPayload {
0283     void clear() { payload = nullptr; }
0284 
0285     const holder_type *ptr() const { return payload; }
0286 
0287     const holder_type *payload{nullptr};
0288   };
0289 
0290   // Payload object with a deleter
0291   struct OwningPayload {
0292     void clear() { payload.reset(); }
0293 
0294     const holder_type *ptr() const { return payload.get(); }
0295 
0296     std::unique_ptr<const holder_type, deleter_type> payload{nullptr,
0297                                                              &noopDeleter};
0298   };
0299 
0300   /// Stores the instance pointer and maybe a deleter
0301   std::conditional_t<kOwnership == DelegateType::NonOwning, NonOwningPayload,
0302                      OwningPayload>
0303       m_payload;
0304 
0305   /// @endcond
0306 
0307   /// Stores the function pointer wrapping the compile time function pointer given in @c connect().
0308   function_type m_function{nullptr};
0309 };
0310 
0311 template <typename, typename H = void>
0312 class OwningDelegate;
0313 
0314 /// Alias for an owning delegate
0315 template <typename R, typename H, typename... Args>
0316 class OwningDelegate<R(Args...), H>
0317     : public Delegate<R(Args...), H, DelegateType::Owning> {};
0318 
0319 }  // namespace Acts