Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Delegate.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Delegate.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
12 
13 #include <cassert>
14 #include <functional>
15 #include <memory>
16 #include <type_traits>
17 
18 namespace Acts {
19 
21 enum class DelegateType { Owning, NonOwning };
22 
23 // Specialization needed for defaulting ownership and for R(Args...) syntax
24 template <typename, typename H = void, DelegateType = DelegateType::NonOwning>
25 class Delegate;
26 
41 template <typename R, typename H, DelegateType O, typename... Args>
42 class Delegate<R(Args...), H, O> {
43  static constexpr DelegateType kOwnership = O;
44 
46  using return_type = R;
47  using holder_type = H;
49  using function_type = return_type (*)(const holder_type *, Args...);
50 
52 
53  using deleter_type = void (*)(const holder_type *);
54 
55  template <typename T, typename C>
56  using isSignatureCompatible =
57  decltype(std::declval<T &>() = std::declval<C>());
58 
59  using OwningDelegate =
60  Delegate<R(Args...), holder_type, DelegateType::Owning>;
61  using NonOwningDelegate =
62  Delegate<R(Args...), holder_type, DelegateType::NonOwning>;
63  template <typename T>
64  using isNoFunPtr = std::enable_if_t<
65  not std::is_convertible_v<std::decay_t<T>, function_type> and
66  not std::is_same_v<std::decay_t<T>, OwningDelegate> and
67  not std::is_same_v<std::decay_t<T>, NonOwningDelegate>>;
68 
69  public:
70  Delegate() = default;
71 
72  Delegate(Delegate &&) = default;
73  Delegate &operator=(Delegate &&) = default;
74  Delegate(const Delegate &) = default;
75  Delegate &operator=(const Delegate &) = default;
76 
82  Delegate(function_type callable) { connect(callable); }
83 
89  template <typename Callable, typename = isNoFunPtr<Callable>>
90  Delegate(Callable &callable) {
91  connect(callable);
92  }
93 
96  template <typename Callable, typename = isNoFunPtr<Callable>>
97  Delegate(Callable &&) = delete;
98 
104  void operator=(function_type callable) { connect(callable); }
105 
111  template <typename Callable, typename = isNoFunPtr<Callable>>
112  void operator=(Callable &callable) {
113  connect(callable);
114  }
115 
118  template <typename Callable, typename = isNoFunPtr<Callable>>
119  void operator=(Callable &&) = delete;
120 
124  template <auto Callable>
125  void connect() {
126  m_payload.payload = nullptr;
127 
128  static_assert(
130  decltype(Callable)>::value,
131  "Callable given does not correspond exactly to required call "
132  "signature");
133 
134  m_function = [](const holder_type * /*payload*/,
135  Args... args) -> return_type {
136  return std::invoke(Callable, std::forward<Args>(args)...);
137  };
138  }
139 
145  template <typename Callable, typename = isNoFunPtr<Callable>>
146  void connect(Callable &callable) {
147  connect<&Callable::operator(), Callable>(&callable);
148  }
149 
152  template <typename Callable, typename = isNoFunPtr<Callable>>
153  void connect(Callable &&) = delete;
154 
160  void connect(function_type callable) {
161  if constexpr (kOwnership == DelegateType::NonOwning) {
162  m_payload.payload = nullptr;
163  }
164  m_function = callable;
165  }
166 
173  template <auto Callable, typename Type, DelegateType T = kOwnership,
174  typename = std::enable_if_t<T == DelegateType::NonOwning>>
175  void connect(const Type *instance) {
176  using member_ptr_type = return_type (Type::*)(Args...) const;
177 
178  static_assert(Concepts::is_detected<isSignatureCompatible, member_ptr_type,
179  decltype(Callable)>::value,
180  "Callable given does not correspond exactly to required call "
181  "signature");
182 
183  m_payload.payload = instance;
184 
185  m_function = [](const holder_type *payload, Args... args) -> return_type {
186  assert(payload != nullptr && "Payload is required, but not set");
187  const auto *concretePayload = static_cast<const Type *>(payload);
188  return std::invoke(Callable, concretePayload,
189  std::forward<Args>(args)...);
190  };
191  }
192 
198  template <auto Callable, typename Type>
199  void connect(std::unique_ptr<const Type> instance) {
200  using member_ptr_type = return_type (Type::*)(Args...) const;
201  static_assert(Concepts::is_detected<isSignatureCompatible, member_ptr_type,
202  decltype(Callable)>::value,
203  "Callable given does not correspond exactly to required call "
204  "signature");
205 
206  if constexpr (kOwnership == DelegateType::Owning) {
207  m_payload.payload = std::unique_ptr<const holder_type, deleter_type>(
208  instance.release(), [](const holder_type *payload) {
209  const auto *concretePayload = static_cast<const Type *>(payload);
210  delete concretePayload;
211  });
212  } else {
213  m_payload.payload = instance.release();
214  }
215 
216  m_function = [](const holder_type *payload, Args... args) -> return_type {
217  assert(payload != nullptr && "Payload is required, but not set");
218  const auto *concretePayload = static_cast<const Type *>(payload);
219  return std::invoke(Callable, concretePayload,
220  std::forward<Args>(args)...);
221  };
222  }
223 
228  assert(connected() && "Delegate is not connected");
229  return std::invoke(m_function, m_payload.ptr(),
230  std::forward<Args>(args)...);
231  }
232 
235  bool connected() const { return m_function != nullptr; }
236 
239  operator bool() const { return connected(); }
240 
242  void disconnect() {
243  m_payload.clear();
244  m_function = nullptr;
245  }
246 
247  template <typename holder_t = holder_type,
248  typename = std::enable_if_t<!std::is_same_v<holder_t, void>>>
249  const holder_type *instance() const {
250  return m_payload.ptr();
251  }
252 
253  private:
254  // Deleter that does not do anything
255  static void noopDeleter(const holder_type * /*unused*/) {}
256 
258 
259  // Payload object without a deleter
260  struct NonOwningPayload {
261  void clear() { payload = nullptr; }
262 
263  const holder_type *ptr() const { return payload; }
264 
265  const holder_type *payload{nullptr};
266  };
267 
268  // Payload object with a deleter
269  struct OwningPayload {
270  void clear() { payload.reset(); }
271 
272  const holder_type *ptr() const { return payload.get(); }
273 
274  std::unique_ptr<const holder_type, deleter_type> payload{nullptr,
275  &noopDeleter};
276  };
277 
279  std::conditional_t<kOwnership == DelegateType::NonOwning, NonOwningPayload,
280  OwningPayload>
281  m_payload;
282 
284 
286  function_type m_function{nullptr};
287 };
288 
289 template <typename, typename H = void>
291 
293 template <typename R, typename H, typename... Args>
294 class OwningDelegate<R(Args...), H>
295  : public Delegate<R(Args...), H, DelegateType::Owning> {};
296 
297 } // namespace Acts