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 (c) 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 private:
36  const invoke_function_t& m_InvokeFunction;
37 };
38 
39 template<class return_t, class function_t, class invoke_function_t>
40 inline return_t invoker<return_t, function_t, invoke_function_t>::invoke(const function_t& function) noexcept
41 {
42  return m_InvokeFunction(function);
43 }
44 
45 template<class derived_t, class return_t, class... args_t>
46 base_delegate<derived_t, return_t, args_t...>::base_delegate(base_delegate&& other) noexcept
47 {
48  std::swap(m_optFunctions, other.m_optFunctions);
49  std::swap(m_pDelegateAliveMarker, other.m_pDelegateAliveMarker);
50 }
51 
52 template<class derived_t, class return_t, class... args_t>
53 typename base_delegate<derived_t, return_t, args_t...>::base_delegate& base_delegate<derived_t, return_t, args_t...>::
54  operator=(base_delegate&& other) noexcept
55 {
56  std::swap(m_optFunctions, other.m_optFunctions);
57  std::swap(m_pDelegateAliveMarker, other.m_pDelegateAliveMarker);
58  return *this;
59 }
60 
61 template<class derived_t, class return_t, class... args_t>
62 template<class... creation_args_t>
63 derived_t base_delegate<derived_t, return_t, args_t...>::create_singlecast(creation_args_t... args) noexcept
64 {
65  derived_t delegate;
66  if constexpr (requires(derived_t d) { d.add_weak(args...); })
67  delegate.add_weak(args...);
68  if constexpr (requires(derived_t d) { d.add_token(args...); })
69  delegate.add_token(args...);
70  else
71  QX_STATIC_ASSERT_NO_INSTANTIATION("No overload that takes these args");
72 
73  return delegate;
74 }
75 
76 template<class derived_t, class return_t, class... args_t>
77 template<callable_c<return_t, args_t...> callable_t>
79  callable_t callable,
80  priority ePriority) noexcept
81 {
82  time_ordered_priority_key key(ePriority);
83  // SSO depends on the size of the input callable
84  add_function(key, std::move(callable));
85  return key;
86 }
87 
88 template<class derived_t, class return_t, class... args_t>
89 template<class object_t>
91  object_t& object,
92  return_t (object_t::*pMethod)(args_t...),
93  priority ePriority) noexcept
94 {
95  return add_token(
96  // 2 pointers, SOO on all compilers
97  [pMethod, &object](args_t... args)
98  {
99  return (object.*pMethod)(std::forward<args_t>(args)...);
100  },
101  ePriority);
102 }
103 
104 template<class derived_t, class return_t, class... args_t>
105 template<callable_c<return_t, args_t...> callable_t>
107  callable_t callable,
108  priority ePriority) noexcept
109 {
110  // SSO depends on the size of the input callable
111  return add_destruction_callback(time_ordered_priority_key(ePriority), function_type(std::move(callable)));
112 }
113 
114 template<class derived_t, class return_t, class... args_t>
115 template<class object_t>
117  object_t& object,
118  return_t (object_t::*pMethod)(args_t...),
119  priority ePriority) noexcept
120 {
121  return add_destruction_callback(
122  time_ordered_priority_key(ePriority),
123  // 2 pointers, SOO on all compilers
124  [&object, pMethod](args_t... args)
125  {
126  return (object.*pMethod)(std::forward<args_t>(args)...);
127  });
128 }
129 
130 template<class derived_t, class return_t, class... args_t>
131 template<class object_t, callable_c<return_t, args_t...> callable_t>
133  std::weak_ptr<object_t> pWeakObject,
134  callable_t callable,
135  priority ePriority) noexcept
136 {
137  time_ordered_priority_key key(ePriority);
138  add_weak(
139  key,
140  std::move(pWeakObject),
141  // SSO depends on the size of the input callable
142  [callable_ = std::move(callable)](object_t*, args_t... args)
143  {
144  return callable_(std::forward<args_t>(args)...);
145  });
146  return key;
147 }
148 
149 template<class derived_t, class return_t, class... args_t>
150 template<class object_t>
152  std::weak_ptr<object_t> pWeakObject,
153  return_t (object_t::*pMethod)(args_t...),
154  priority ePriority) noexcept
155 {
156  time_ordered_priority_key key(ePriority);
157  add_weak(
158  key,
159  std::move(pWeakObject),
160  // 1 pointer, SOO on all compilers
161  [pMethod](object_t* pObject, args_t... args)
162  {
163  return (pObject->*pMethod)(std::forward<args_t>(args)...);
164  });
165  return key;
166 }
167 
168 template<class derived_t, class return_t, class... args_t>
170 {
171  return m_optFunctions
172  && std::visit(
173  [this, token]<class T>(T& value)
174  {
175  if constexpr (std::is_same_v<T, single_value_type>)
176  {
177  if (value.first == token)
178  {
179  m_optFunctions.reset();
180  return true;
181  }
182  else
183  {
184  return false;
185  }
186  }
187  else if constexpr (std::is_same_v<T, container_type>)
188  {
189  return value.erase(token) == 1;
190  }
191  else
192  {
193  QX_STATIC_ASSERT_NO_INSTANTIATION("Invalid type");
194  return false;
195  }
196  },
197  *m_optFunctions);
198 }
199 
200 template<class derived_t, class return_t, class... args_t>
202 {
203  m_optFunctions.reset();
204 }
205 
206 template<class derived_t, class return_t, class... args_t>
208 {
209  if (!m_optFunctions)
210  return 0;
211 
212  return std::visit(
213  []<class T>(const T& value) -> size_t
214  {
215  if constexpr (std::is_same_v<T, single_value_type>)
216  {
217  return 1;
218  }
219  else if constexpr (std::is_same_v<T, container_type>)
220  {
221  return value.size();
222  }
223  else
224  {
225  QX_STATIC_ASSERT_NO_INSTANTIATION("Invalid type");
226  return 0;
227  }
228  },
229  *m_optFunctions);
230 }
231 
232 template<class derived_t, class return_t, class... args_t>
234 {
235  return size() == 0;
236 }
237 
238 template<class derived_t, class return_t, class... args_t>
239 template<class invoke_single_t, class invoke_multiple_t>
241  const invoke_single_t& invokeSingle,
242  const invoke_multiple_t& invokeMultiple) const noexcept
243 {
244  if (!m_optFunctions)
245  {
246  if constexpr (std::is_void_v<return_t>)
247  return;
248  else
249  return return_t {};
250  }
251 
252  // modifying during iteration protection
253  thread_local variant_type tempFunctions;
254  tempFunctions = *m_optFunctions;
255 
256  if (std::holds_alternative<single_value_type>(tempFunctions))
257  {
258  const auto& [_, function] = std::get<single_value_type>(tempFunctions);
259  return invokeSingle(function);
260  }
261  else
262  {
263  const container_type& container = std::get<container_type>(tempFunctions);
264 
265  invoker<return_t, function_type, invoke_single_t> invokerSingle(invokeSingle);
266  invoker<return_t, function_type, invoke_multiple_t> invokerMultiple(invokeMultiple);
268  if (container.size() == 1)
269  invoker = &invokerSingle;
270  else
271  invoker = &invokerMultiple;
272 
273  if constexpr (std::is_void_v<return_t>)
274  {
275  for (const auto& [_, function] : container)
276  invoker->invoke(function);
277 
278  return;
279  }
280  else if constexpr (delegate_pipe_c<return_t>)
281  {
282  return_t result {};
283  for (const auto& [_, function] : container)
284  result = result | invoker->invoke(function);
285 
286  return result;
287  }
288  else
289  {
290  return_t result {};
291  for (const auto& [_, function] : container)
292  result = invoker->invoke(function);
293 
294  return result;
295  }
296  }
297 }
298 
299 template<class derived_t, class return_t, class... args_t>
300 template<class object_t, callable_c<return_t, object_t*, args_t...> callable_t>
303  std::weak_ptr<object_t> pWeakObject,
304  callable_t callable) noexcept
305 {
306  add_function(
307  key,
308  [this, key, pWeakObject, callable_ = std::move(callable)](args_t... args)
309  {
310  if (std::shared_ptr<object_t> pObject = pWeakObject.lock())
311  {
312  return callable_(pObject.get(), std::forward<args_t>(args)...);
313  }
314  else
315  {
316  remove(key);
317  return return_t();
318  }
319  });
320 }
321 
322 template<class derived_t, class return_t, class... args_t>
323 destruction_callback base_delegate<derived_t, return_t, args_t...>::add_destruction_callback(
324  time_ordered_priority_key key,
325  function_type value) noexcept
326 {
327  add_function(key, std::move(value));
328 
329  if (!m_pDelegateAliveMarker)
330  m_pDelegateAliveMarker = std::make_shared<bool>(true);
331 
332  return [this, key, pDelegateAliveMarker = std::weak_ptr(m_pDelegateAliveMarker)]()
333  {
334  if (!pDelegateAliveMarker.expired())
335  remove(key);
336  };
337 }
338 
339 template<class derived_t, class return_t, class... args_t>
340 void base_delegate<derived_t, return_t, args_t...>::add_function(
341  time_ordered_priority_key key,
342  function_type function) noexcept
343 {
344  if (!m_optFunctions)
345  {
346  m_optFunctions = variant_type(single_value_type(key, std::move(function)));
347  }
348  else if (std::holds_alternative<single_value_type>(*m_optFunctions))
349  {
350  single_value_type first = std::get<single_value_type>(std::move(*m_optFunctions));
351  container_type container;
352  container.try_emplace(first.first, std::move(first.second));
353  container.try_emplace(key, std::move(function));
354  m_optFunctions = std::move(container);
355  }
356  else
357  {
358  std::get<container_type>(*m_optFunctions).try_emplace(key, std::move(function));
359  }
360 }
361 
362 } // namespace details
363 
364 template<class return_t, class... args_t>
365  requires(sizeof...(args_t) > 0 && (!std::is_void_v<args_t> && ...))
366 return_t delegate<return_t(args_t...)>::execute(args_t... args) const noexcept
367 {
368  return this->execute_internal(
369  [&args...](const typename super_type::function_type& function)
370  {
371  return function(std::forward<args_t>(args)...);
372  },
373  [&args...](const typename super_type::function_type& function)
374  {
375  return function(args...);
376  });
377 }
378 
379 template<class return_t>
380 return_t delegate<return_t(void)>::execute() const noexcept
381 {
382  return this->execute_internal(
383  [](const typename super_type::function_type& function)
384  {
385  return function();
386  },
387  [](const typename super_type::function_type& function)
388  {
389  return function();
390  });
391 }
392 
393 } // namespace qx
Single or multicast delegate.
Definition: delegate.h:66
Class for RAII: functor passed in constructor will be called in destructor.
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:132
return_t execute_internal(const invoke_single_t &invokeSingle, const invoke_multiple_t &invokeMultiple) const noexcept
Execute all callables.
Definition: delegate.inl:240
void clear() noexcept
Clear all the callables in this delegate.
Definition: delegate.inl:201
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:106
bool empty() const noexcept
Check if this delegate is empty.
Definition: delegate.inl:233
size_t size() const noexcept
Get the number of functions bound to this delegate.
Definition: delegate.inl:207
static derived_t create_singlecast(creation_args_t... args) noexcept
Create a singlecast delegate.
Definition: delegate.inl:63
bool remove(delegate_token_type token) noexcept
Remove a callable using its token.
Definition: delegate.inl:169
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:78
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.