qxLib
delegate.inl
Go to the documentation of this file.
1 /**
2 
3  @file delegate.inl
4  @author Khrapov
5  @date 7.01.2024
6  @copyright © Nick Khrapov, 2024. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 namespace details
14 {
15 
16 template<class return_t, class function_t>
18 {
19 public:
20  virtual ~base_invoker() noexcept = default;
21  virtual return_t invoke(const function_t& function) noexcept = 0;
22 };
23 
24 template<class return_t, class function_t, class invoke_function_t>
25 class invoker : public base_invoker<return_t, function_t>
26 {
27 public:
28  invoker(const invoke_function_t& invoke) noexcept : m_InvokeFunction(invoke)
29  {
30  }
31 
32 private:
33  virtual return_t invoke(const function_t& function) noexcept override
34  {
35  return m_InvokeFunction(function);
36  }
37 
38 private:
39  const invoke_function_t& m_InvokeFunction;
40 };
41 
42 template<class derived_t, class return_t, class... args_t>
44 {
45  std::swap(m_optFunctions, other.m_optFunctions);
46  std::swap(m_pDelegateAliveMarker, other.m_pDelegateAliveMarker);
47 }
48 
49 template<class derived_t, class return_t, class... args_t>
50 typename base_delegate<derived_t, return_t, args_t...>::base_delegate& base_delegate<derived_t, return_t, args_t...>::
51  operator=(base_delegate&& other) noexcept
52 {
53  std::swap(m_optFunctions, other.m_optFunctions);
54  std::swap(m_pDelegateAliveMarker, other.m_pDelegateAliveMarker);
55  return *this;
56 }
57 
58 template<class derived_t, class return_t, class... args_t>
59 template<class... creation_args_t>
60 derived_t base_delegate<derived_t, return_t, args_t...>::create_singlecast(creation_args_t... args) noexcept
61 {
62  derived_t delegate;
63  if constexpr (requires(derived_t d) { d.add_weak(args...); })
64  delegate.add_weak(args...);
65  if constexpr (requires(derived_t d) { d.add_token(args...); })
66  delegate.add_token(args...);
67  else
68  QX_STATIC_ASSERT_NO_INSTANTIATION("No overload that takes these args");
69 
70  return delegate;
71 }
72 
73 template<class derived_t, class return_t, class... args_t>
74 template<callable_c<return_t, args_t...> callable_t>
76  callable_t callable,
77  priority ePriority) noexcept
78 {
79  time_ordered_priority_key key(ePriority);
80  // SSO depends on the size of the input callable
81  add_function(key, std::move(callable));
82  return key;
83 }
84 
85 template<class derived_t, class return_t, class... args_t>
86 template<class object_t>
88  object_t& object,
89  return_t (object_t::*pMethod)(args_t...),
90  priority ePriority) noexcept
91 {
92  return add_token(
93  // 2 pointers, SOO on all compilers
94  [pMethod, &object](args_t... args)
95  {
96  return (object.*pMethod)(std::forward<args_t>(args)...);
97  },
98  ePriority);
99 }
100 
101 template<class derived_t, class return_t, class... args_t>
102 template<callable_c<return_t, args_t...> callable_t>
104  callable_t callable,
105  priority ePriority) noexcept
106 {
107  // SSO depends on the size of the input callable
108  return add_destruction_callback(time_ordered_priority_key(ePriority), function_type(std::move(callable)));
109 }
110 
111 template<class derived_t, class return_t, class... args_t>
112 template<class object_t>
114  object_t& object,
115  return_t (object_t::*pMethod)(args_t...),
116  priority ePriority) noexcept
117 {
118  return add_destruction_callback(
119  time_ordered_priority_key(ePriority),
120  // 2 pointers, SOO on all compilers
121  [&object, pMethod](args_t... args)
122  {
123  return (object.*pMethod)(std::forward<args_t>(args)...);
124  });
125 }
126 
127 template<class derived_t, class return_t, class... args_t>
128 template<class object_t, callable_c<return_t, args_t...> callable_t>
130  std::weak_ptr<object_t> pWeakObject,
131  callable_t callable,
132  priority ePriority) noexcept
133 {
134  time_ordered_priority_key key(ePriority);
135  add_weak(
136  key,
137  std::move(pWeakObject),
138  // SSO depends on the size of the input callable
139  [callable_ = std::move(callable)](object_t*, args_t... args)
140  {
141  return callable_(std::forward<args_t>(args)...);
142  });
143  return key;
144 }
145 
146 template<class derived_t, class return_t, class... args_t>
147 template<class object_t>
149  std::weak_ptr<object_t> pWeakObject,
150  return_t (object_t::*pMethod)(args_t...),
151  priority ePriority) noexcept
152 {
153  time_ordered_priority_key key(ePriority);
154  add_weak(
155  key,
156  std::move(pWeakObject),
157  // 1 pointer, SOO on all compilers
158  [pMethod](object_t* pObject, args_t... args)
159  {
160  return (pObject->*pMethod)(std::forward<args_t>(args)...);
161  });
162  return key;
163 }
164 
165 template<class derived_t, class return_t, class... args_t>
167 {
168  return m_optFunctions
169  && std::visit(
170  [this, token]<class T>(T& value)
171  {
172  if constexpr (std::is_same_v<T, single_value_type>)
173  {
174  if (value.first == token)
175  {
176  m_optFunctions.reset();
177  return true;
178  }
179  else
180  {
181  return false;
182  }
183  }
184  else if constexpr (std::is_same_v<T, container_type>)
185  {
186  return value.erase(token) == 1;
187  }
188  else
189  {
190  QX_STATIC_ASSERT_NO_INSTANTIATION("Invalid type");
191  return false;
192  }
193  },
194  *m_optFunctions);
195 }
196 
197 template<class derived_t, class return_t, class... args_t>
199 {
200  m_optFunctions.reset();
201 }
202 
203 template<class derived_t, class return_t, class... args_t>
205 {
206  if (!m_optFunctions)
207  return 0;
208 
209  return std::visit(
210  []<class T>(const T& value) -> size_t
211  {
212  if constexpr (std::is_same_v<T, single_value_type>)
213  {
214  return 1;
215  }
216  else if constexpr (std::is_same_v<T, container_type>)
217  {
218  return value.size();
219  }
220  else
221  {
222  QX_STATIC_ASSERT_NO_INSTANTIATION("Invalid type");
223  return 0;
224  }
225  },
226  *m_optFunctions);
227 }
228 
229 template<class derived_t, class return_t, class... args_t>
231 {
232  return size() == 0;
233 }
234 
235 template<class derived_t, class return_t, class... args_t>
236 template<class invoke_single_t, class invoke_multiple_t>
238  const invoke_single_t& invokeSingle,
239  const invoke_multiple_t& invokeMultiple) const noexcept
240 {
241  if (!m_optFunctions)
242  {
243  if constexpr (std::is_void_v<return_t>)
244  return;
245  else
246  return return_t {};
247  }
248 
249  // modifying during iteration protection
250  thread_local variant_type tempFunctions;
251  tempFunctions = *m_optFunctions;
252 
253  if (std::holds_alternative<single_value_type>(tempFunctions))
254  {
255  const auto& [_, function] = std::get<single_value_type>(tempFunctions);
256  return invokeSingle(function);
257  }
258  else
259  {
260  const container_type& container = std::get<container_type>(tempFunctions);
261 
262  invoker<return_t, function_type, invoke_single_t> invokerSingle(invokeSingle);
263  invoker<return_t, function_type, invoke_multiple_t> invokerMultiple(invokeMultiple);
265  if (container.size() == 1)
266  invoker = &invokerSingle;
267  else
268  invoker = &invokerMultiple;
269 
270  if constexpr (std::is_void_v<return_t>)
271  {
272  for (const auto& [_, function] : container)
273  invoker->invoke(function);
274 
275  return;
276  }
277  else if constexpr (delegate_pipe_c<return_t>)
278  {
279  return_t result {};
280  for (const auto& [_, function] : container)
281  result = result | invoker->invoke(function);
282 
283  return result;
284  }
285  else
286  {
287  return_t result {};
288  for (const auto& [_, function] : container)
289  result = invoker->invoke(function);
290 
291  return result;
292  }
293  }
294 }
295 
296 template<class derived_t, class return_t, class... args_t>
297 template<class object_t, callable_c<return_t, object_t*, args_t...> callable_t>
300  std::weak_ptr<object_t> pWeakObject,
301  callable_t callable) noexcept
302 {
303  add_function(
304  key,
305  [this, key, pWeakObject, callable_ = std::move(callable)](args_t... args)
306  {
307  if (std::shared_ptr<object_t> pObject = pWeakObject.lock())
308  {
309  return callable_(pObject.get(), std::forward<args_t>(args)...);
310  }
311  else
312  {
313  remove(key);
314  return return_t();
315  }
316  });
317 }
318 
319 template<class derived_t, class return_t, class... args_t>
320 destruction_callback base_delegate<derived_t, return_t, args_t...>::add_destruction_callback(
321  time_ordered_priority_key key,
322  function_type value) noexcept
323 {
324  add_function(key, std::move(value));
325 
326  if (!m_pDelegateAliveMarker)
327  m_pDelegateAliveMarker = std::make_shared<bool>(true);
328 
329  return [this, key, pDelegateAliveMarker = std::weak_ptr(m_pDelegateAliveMarker)]()
330  {
331  if (!pDelegateAliveMarker.expired())
332  remove(key);
333  };
334 }
335 
336 template<class derived_t, class return_t, class... args_t>
337 void base_delegate<derived_t, return_t, args_t...>::add_function(
338  time_ordered_priority_key key,
339  function_type function) noexcept
340 {
341  if (!m_optFunctions)
342  {
343  m_optFunctions = variant_type(single_value_type(key, std::move(function)));
344  }
345  else if (std::holds_alternative<single_value_type>(*m_optFunctions))
346  {
347  single_value_type first = std::get<single_value_type>(std::move(*m_optFunctions));
348  container_type container;
349  container.emplace(first.first, std::move(first.second));
350  container.emplace(key, std::move(function));
351  m_optFunctions = std::move(container);
352  }
353  else
354  {
355  std::get<container_type>(*m_optFunctions).emplace(key, std::move(function));
356  }
357 }
358 
359 } // namespace details
360 
361 template<class return_t, class... args_t>
362  requires(sizeof...(args_t) > 0 && (!std::is_void_v<args_t> && ...))
363 return_t delegate<return_t(args_t...)>::execute(args_t... args) const noexcept
364 {
365  return this->execute_internal(
366  [&args...](const typename super_type::function_type& function)
367  {
368  return function(std::forward<args_t>(args)...);
369  },
370  [&args...](const typename super_type::function_type& function)
371  {
372  return function(args...);
373  });
374 }
375 
376 template<class return_t>
377 return_t delegate<return_t(void)>::execute() const noexcept
378 {
379  return this->execute_internal(
380  [](const typename super_type::function_type& function)
381  {
382  return function();
383  },
384  [](const typename super_type::function_type& function)
385  {
386  return function();
387  });
388 }
389 
390 } // namespace qx
Single or multicast delegate.
Definition: delegate.h:66
Class for RAII: functor passed in constructor will be called in destructor.
Base delegate type.
Definition: delegate.h:84
delegate_token_type add_weak(std::weak_ptr< object_t > pWeakObject, callable_t callable, priority ePriority=priority::normal) noexcept
Add a callable that will be executed only if the appropriate weak object is valid.
Definition: delegate.inl:129
return_t execute_internal(const invoke_single_t &invokeSingle, const invoke_multiple_t &invokeMultiple) const noexcept
Execute all callables.
Definition: delegate.inl:237
void clear() noexcept
Clear all the callables in this delegate.
Definition: delegate.inl:198
destruction_callback add_destruction_callback(callable_t callable, priority ePriority=priority::normal) noexcept
Add a callable that will be removed from the delegate when its destruction callback is destroyed.
Definition: delegate.inl:103
bool empty() const noexcept
Check if this delegate is empty.
Definition: delegate.inl:230
size_t size() const noexcept
Get the number of functions bound to this delegate.
Definition: delegate.inl:204
static derived_t create_singlecast(creation_args_t... args) noexcept
Create a singlecast delegate.
Definition: delegate.inl:60
bool remove(delegate_token_type token) noexcept
Remove a callable using its token.
Definition: delegate.inl:166
delegate_token_type add_token(callable_t callable, priority ePriority=priority::normal) noexcept
Add a callable using a token with manual unsubscribing.
Definition: delegate.inl:75
A class that can be used as a key in ordered containers so that items are ordered in descending order...
Definition: priority.h:47
requires(same_variadic_args_v< args_t... >) const expr auto coalesce(args_t &&... args)
Coalesce function, C# a ?? b analogue.
Definition: coalesce.inl:57
priority
User may use the predefined values or the custom ones, for ex. "normal - 1", this type is supposed to...
Definition: priority.h:27
#define QX_STATIC_ASSERT_NO_INSTANTIATION(Message)
This static assert will fail if block it placed in must not be instantiated.