Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:10

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   static constexpr DelegateType kOwnership = O;
0049 
0050   /// Alias of the return type
0051   using return_type = R;
0052   using holder_type = H;
0053   using function_type = return_type (*)(const holder_type *, Args...);
0054   using function_ptr_type = return_type (*)(Args...);
0055   using signature_type = R(Args...);
0056 
0057   using deleter_type = void (*)(const holder_type *);
0058 
0059  private:
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 
0069   template <typename T>
0070   using isNoFunPtr = std::conjunction<
0071       std::negation<std::is_convertible<std::decay_t<T>, function_type>>,
0072       std::negation<std::is_same<std::decay_t<T>, OwningDelegate>>,
0073       std::negation<std::is_same<std::decay_t<T>, NonOwningDelegate>>>;
0074 
0075  public:
0076   Delegate() = default;
0077 
0078   Delegate(Delegate &&) noexcept = default;
0079   Delegate &operator=(Delegate &&) noexcept = default;
0080   Delegate(const Delegate &) noexcept = default;
0081   Delegate &operator=(const Delegate &) noexcept = default;
0082 
0083   /// @cond
0084   /// Constructor with an explicit runtime callable
0085   /// @param callable The runtime value of the callable
0086   /// @note The function signature requires the first argument of the callable is `const void*`.
0087   ///       i.e. if the signature of the delegate is `void(int)`, the
0088   ///       callable's signature has to be `void(const void*, int)`.
0089   explicit Delegate(function_type callable) { connect(callable); }
0090   /// @endcond
0091 
0092   /// Constructor with a possibly stateful function object.
0093   /// @tparam Callable Type of the callable
0094   /// @param callable The callable (function object or lambda)
0095   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0096   ///       it's lifetime is longer than that of @c Delegate.
0097   template <typename Callable>
0098   explicit Delegate(Callable &callable)
0099     requires(isNoFunPtr<Callable>::value)
0100   {
0101     connect(callable);
0102   }
0103 
0104   /// Constructor with a compile-time free function pointer
0105   /// @tparam Callable The compile-time free function pointer
0106   /// @note @c DelegateFuncTag is used to communicate the callable type
0107   template <auto Callable>
0108   explicit Delegate(DelegateFuncTag<Callable> /*tag*/) {
0109     connect<Callable>();
0110   }
0111 
0112   /// Constructor with a compile-time member function pointer and instance
0113   /// @tparam Callable The compile-time member function pointer
0114   /// @tparam Type The type of the instance the member function should be called on
0115   /// @param instance The instance on which the member function pointer should be called on
0116   /// @note @c Delegate does not assume owner ship over @p instance.
0117   /// @note @c DelegateFuncTag is used to communicate the callable type
0118   template <auto Callable, typename Type>
0119 
0120   Delegate(DelegateFuncTag<Callable> /*tag*/, const Type *instance)
0121     requires(kOwnership == DelegateType::NonOwning)
0122   {
0123     connect<Callable>(instance);
0124   }
0125 
0126   /// Constructor from rvalue reference is deleted, should catch construction
0127   /// with temporary objects and thus invalid pointers
0128   template <typename Callable>
0129   Delegate(Callable &&)
0130     requires(isNoFunPtr<Callable>::value)
0131   = delete;
0132 
0133   /// @cond
0134   /// Assignment operator with an explicit runtime callable
0135   /// @param callable The runtime value of the callable
0136   /// @note The function signature requires the first argument of the callable is `const void*`.
0137   ///       i.e. if the signature of the delegate is `void(int)`, the
0138   ///       callable's signature has to be `void(const void*, int)`.
0139   void operator=(function_type callable) { connect(callable); }
0140 
0141   /// Assignment operator with possibly stateful function object.
0142   /// @tparam Callable Type of the callable
0143   /// @param callable The callable (function object or lambda)
0144   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0145   ///       it's lifetime is longer than that of @c Delegate.
0146   template <typename Callable>
0147   void operator=(Callable &callable)
0148     requires(isNoFunPtr<Callable>::value)
0149   {
0150     connect(callable);
0151   }
0152   /// @endcond
0153 
0154   /// Assignment operator from rvalue reference is deleted, should catch
0155   /// assignment from temporary objects and thus invalid pointers
0156   template <typename Callable>
0157   void operator=(Callable &&)
0158     requires(isNoFunPtr<Callable>::value)
0159   = delete;
0160 
0161   /// @cond
0162   /// Connect a free function pointer.
0163   /// @note The function pointer must be ``constexpr`` for @c Delegate to accept it
0164   /// @tparam Callable The compile-time free function pointer
0165   template <auto Callable>
0166   void connect()
0167     requires(
0168         Concepts::invocable_and_returns<Callable, return_type, Args && ...>)
0169   {
0170     m_payload.payload = nullptr;
0171 
0172     static_assert(
0173         Concepts::invocable_and_returns<Callable, return_type, Args &&...>,
0174         "Callable given does not correspond exactly to required call "
0175         "signature");
0176 
0177     m_function = [](const holder_type * /*payload*/,
0178                     Args... args) -> return_type {
0179       return std::invoke(Callable, std::forward<Args>(args)...);
0180     };
0181   }
0182   /// @endcond
0183 
0184   /// Assignment operator with possibly stateful function object.
0185   /// @tparam Callable Type of the callable
0186   /// @param callable The callable (function object or lambda)
0187   /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure
0188   ///       it's lifetime is longer than that of @c Delegate.
0189   template <typename Callable>
0190   void connect(Callable &callable)
0191     requires(isNoFunPtr<Callable>::value)
0192   {
0193     connect<&Callable::operator(), Callable>(&callable);
0194   }
0195 
0196   /// Connection with rvalue reference is deleted, should catch assignment
0197   /// from temporary objects and thus invalid pointers
0198   template <typename Callable>
0199   void connect(Callable &&)
0200     requires(isNoFunPtr<Callable>::value)
0201   = delete;
0202 
0203   /// @cond
0204   /// Connect anything that is assignable to the function pointer
0205   /// @param callable The runtime value of the callable
0206   /// @note The function signature requires the first argument of the callable is `const void*`.
0207   ///       i.e. if the signature of the delegate is `void(int)`, the
0208   ///       callable's signature has to be `void(const void*, int)`.
0209   void connect(function_type callable) {
0210     if constexpr (kOwnership == DelegateType::NonOwning) {
0211       m_payload.payload = nullptr;
0212     }
0213     m_function = callable;
0214   }
0215   /// @endcond
0216 
0217   template <typename Type>
0218   void connect(function_type callable, const Type *instance)
0219     requires(kOwnership == DelegateType::NonOwning)
0220   {
0221     m_payload.payload = instance;
0222     m_function = callable;
0223   }
0224 
0225   /// Connect a member function to be called on an instance
0226   /// @tparam Callable The compile-time member function pointer
0227   /// @tparam Type The type of the instance the member function should be called on
0228   /// @param instance The instance on which the member function pointer should be called on
0229   /// @note @c Delegate does not assume owner ship over @p instance. You need to ensure
0230   ///       it's lifetime is longer than that of @c Delegate.
0231   template <auto Callable, typename Type>
0232   void connect(const Type *instance)
0233     requires(kOwnership == DelegateType::NonOwning &&
0234              Concepts::invocable_and_returns<Callable, return_type, Type,
0235                                              Args && ...>)
0236 
0237   {
0238     m_payload.payload = instance;
0239 
0240     m_function = [](const holder_type *payload, Args... args) -> return_type {
0241       assert(payload != nullptr && "Payload is required, but not set");
0242       const auto *concretePayload = static_cast<const Type *>(payload);
0243       return std::invoke(Callable, concretePayload,
0244                          std::forward<Args>(args)...);
0245     };
0246   }
0247 
0248   /// Connect a member function to be called on an instance
0249   /// @tparam Callable The compile-time member function pointer
0250   /// @tparam Type The type of the instance the member function should be called on
0251   /// @param instance The instance on which the member function pointer should be called on
0252   /// @note @c Delegate assumes owner ship over @p instance.
0253   template <auto Callable, typename Type>
0254   void connect(std::unique_ptr<const Type> instance)
0255     requires(kOwnership == DelegateType::Owning &&
0256              Concepts::invocable_and_returns<Callable, return_type, Type,
0257                                              Args && ...>)
0258   {
0259     m_payload.payload = std::unique_ptr<const holder_type, deleter_type>(
0260         instance.release(), [](const holder_type *payload) {
0261           const auto *concretePayload = static_cast<const Type *>(payload);
0262           delete concretePayload;
0263         });
0264 
0265     m_function = [](const holder_type *payload, Args &&...args) -> return_type {
0266       assert(payload != nullptr && "Payload is required, but not set");
0267       const auto *concretePayload = static_cast<const Type *>(payload);
0268       return std::invoke(Callable, concretePayload,
0269                          std::forward<Args>(args)...);
0270     };
0271   }
0272 
0273   /// The call operator that exposes the functionality of the @c Delegate type.
0274   /// @param args The arguments to call the contained function with
0275   /// @return Return value of the contained function
0276   template <typename... Ts>
0277   return_type operator()(Ts &&...args) const
0278     requires(std::is_invocable_v<function_type, const holder_type *, Ts...>)
0279   {
0280     assert(connected() && "Delegate is not connected");
0281     return std::invoke(m_function, m_payload.ptr(), std::forward<Ts>(args)...);
0282   }
0283 
0284   /// Return whether this delegate is currently connected
0285   /// @return True if this delegate is connected
0286   bool connected() const { return m_function != nullptr; }
0287 
0288   /// Return whether this delegate is currently connected
0289   /// @return True if this delegate is connected
0290   explicit operator bool() const { return connected(); }
0291 
0292   /// Disconnect this delegate, meaning it cannot be called anymore
0293   void disconnect() {
0294     m_payload.clear();
0295     m_function = nullptr;
0296   }
0297 
0298   const holder_type *instance() const
0299     requires(!std::same_as<holder_type, void>)
0300   {
0301     return m_payload.ptr();
0302   }
0303 
0304  private:
0305   // Deleter that does not do anything
0306   static void noopDeleter(const holder_type * /*unused*/) {
0307     // we do not own the payload
0308   }
0309 
0310   /// @cond
0311 
0312   // Payload object without a deleter
0313   struct NonOwningPayload {
0314     void clear() { payload = nullptr; }
0315 
0316     const holder_type *ptr() const { return payload; }
0317 
0318     const holder_type *payload{nullptr};
0319   };
0320 
0321   // Payload object with a deleter
0322   struct OwningPayload {
0323     void clear() { payload.reset(); }
0324 
0325     const holder_type *ptr() const { return payload.get(); }
0326 
0327     std::unique_ptr<const holder_type, deleter_type> payload{nullptr,
0328                                                              &noopDeleter};
0329   };
0330 
0331   /// Stores the instance pointer and maybe a deleter
0332   std::conditional_t<kOwnership == DelegateType::NonOwning, NonOwningPayload,
0333                      OwningPayload>
0334       m_payload;
0335 
0336   /// @endcond
0337 
0338   /// Stores the function pointer wrapping the compile time function pointer given in @c connect().
0339   function_type m_function{nullptr};
0340 };
0341 
0342 template <typename, typename H = void>
0343 class OwningDelegate;
0344 
0345 /// Alias for an owning delegate
0346 template <typename R, typename H, typename... Args>
0347 class OwningDelegate<R(Args...), H>
0348     : public Delegate<R(Args...), H, DelegateType::Owning> {
0349  public:
0350   OwningDelegate() = default;
0351   OwningDelegate(Delegate<R(Args...), H, DelegateType::Owning> &&delegate)
0352       : Delegate<R(Args...), H, DelegateType::Owning>(std::move(delegate)) {}
0353 };
0354 
0355 }  // namespace Acts