qxLib
delegate.h
Go to the documentation of this file.
1 /**
2 
3  @file delegate.h
4  @author Khrapov
5  @date 7.01.2024
6  @copyright © Nick Khrapov, 2024. All right reserved.
7 
8 **/
9 #pragma once
10 
11 #include <functional>
12 #include <map>
13 #include <memory>
14 
17 #include <qx/meta/concepts.h>
18 #include <qx/priority.h>
19 
20 namespace qx
21 {
22 
23 // default constructible pipe element must not change the state of a pipe
24 template<class T>
25 concept delegate_pipe_c = std::is_default_constructible_v<T> && !std::is_arithmetic_v<T> && requires(T t) {
26  { t | t } -> std::convertible_to<T>;
27 };
28 
29 template<class T>
30 concept delegate_return_c = std::is_void_v<T> || delegate_pipe_c<T>;
31 
32 using delegate_token_type = time_ordered_priority_key;
33 
34 /**
35 
36  @class base_delegate
37  @brief Single or multicast delegate. Use the qx::delegate<> class in your code.
38  @details For a singlecast version:
39  1. Create with qx::delegate<...>::create_singlecast(...)
40  2. Call delegate.execute(...)
41  For a multicast version:
42  1. Default construct
43  2. All various callbacks with delegate.add_xxx(...)
44  2. Call delegate.execute(...)
45  @tparam derived_t - CRTP derived class type
46  @tparam return_t - the exact type that all the passed callables and execute() should return
47  @tparam args_t - the exact type that all the passed callables and execute() should take
48  @author Khrapov
49  @date 4.07.2025
50 
51 **/
52 template<class derived_t, delegate_return_c return_t, class... args_t>
54 {
55  friend derived_t;
56 
57  base_delegate() noexcept = default;
58  base_delegate(base_delegate&& other) noexcept;
59  base_delegate(const base_delegate& other) noexcept = delete;
60 
61 public:
62  using function_type = std::function<return_t(args_t...)>;
63 
64 private:
65  using container_type = std::map<time_ordered_priority_key, function_type>;
66 
67 public:
68  base_delegate& operator=(base_delegate&& other) noexcept;
69  base_delegate& operator=(const base_delegate& other) noexcept = delete;
70 
71  /**
72  @brief Create a singlecast delegate
73  @tparam creation_args_t - any arguments that can be used in add_weak or add_free
74  @param args - template parameter pack
75  @retval - created delegate
76  **/
77  template<class... creation_args_t>
78  static derived_t create_singlecast(creation_args_t... args) noexcept;
79 
80  /**
81  @brief Add a callable without any protection
82  @warning If you capture `this` in the lambda passed and the object becomes invalid, it'll crash.
83  Consider add_destruction_callback or add_weak in this case.
84  @tparam callable_t - any callable type: lambda, function, static method pointer, ect
85  @param callable - callable object
86  @param ePriority - callable priority. Callables will be called in order of priority,
87  from highest to lowest, and in the order they were added.
88  @retval - a token that can be used to remove this callable from the delegate
89  **/
90  template<callable_c<return_t, args_t...> callable_t>
91  [[maybe_unused]] delegate_token_type add_free(callable_t callable, priority ePriority = priority::normal) noexcept;
92 
93  /**
94  @brief Add a callable that will be removed from the delegate when its destruction callback is destroyed
95  @tparam callable_t - any callable type: lambda, function, static method pointer, ect
96  @param callable - callable object
97  @param ePriority - callable priority. Callables will be called in order of priority,
98  from highest to lowest, and in the order they were added.
99  @retval - an object that removes this callable from the delegate on its destruction
100  **/
101  template<callable_c<return_t, args_t...> callable_t>
103  callable_t callable,
104  priority ePriority = priority::normal) noexcept;
105 
106  /**
107  @brief Add a callable that will be removed from the delegate when its destruction callback is destroyed
108  @tparam object_t - object type
109  @param object - object reference
110  @param pMethod - object's method pointer
111  @param ePriority - callable priority. Callables will be called in order of priority,
112  from highest to lowest, and in the order they were added.
113  @retval - an object that removes this callable from the delegate on its destruction
114  **/
115  template<class object_t>
117  object_t& object,
118  return_t (object_t::*pMethod)(args_t...),
119  priority ePriority = priority::normal) noexcept;
120 
121  /**
122  @brief Add a callable that will be executed only if the appropriate weak object is valid
123  @tparam object_t - object type
124  @tparam callable_t - any callable type: lambda, function, static method pointer, ect
125  @param pWeakObject - an object to track
126  @param callable - callable object
127  @param ePriority - callable priority. Callables will be called in order of priority,
128  from highest to lowest, and in the order they were added.
129  @retval - a token that can be used to remove this callable from the delegate
130  **/
131  template<class object_t, callable_c<return_t, args_t...> callable_t>
132  [[maybe_unused]] delegate_token_type add_weak(
133  std::weak_ptr<object_t> pWeakObject,
134  callable_t callable,
135  priority ePriority = priority::normal) noexcept;
136 
137  /**
138  @brief Add a callable that will be executed only if the appropriate weak object is valid
139  @tparam object_t - object type
140  @param pWeakObject - an object to track and to apply the method to
141  @param pMethod - object's method pointer
142  @param ePriority - callable priority. Callables will be called in order of priority,
143  from highest to lowest, and in the order they were added.
144  @retval - a token that can be used to remove this callable from the delegate
145  **/
146  template<class object_t>
147  [[maybe_unused]] delegate_token_type add_weak(
148  std::weak_ptr<object_t> pWeakObject,
149  return_t (object_t::*pMethod)(args_t...),
150  priority ePriority = priority::normal) noexcept;
151 
152  /**
153  @brief Remove a callable using its token
154  @param token - callable token
155  @retval - true is a callable was deleted
156  **/
157  bool remove(delegate_token_type token) noexcept;
158 
159  /**
160  @brief Clear all the callables in this delegate
161  **/
162  void clear() noexcept;
163 
164 protected:
165  /**
166  @brief Execute all callables
167  @tparam invoke_single_t - a callable type that executes a delegate callable by moving args into it
168  @tparam invoke_multiple_t - a callable type that executes a delegate callable by copying args into it
169  @param invokeSingle - invoke_single_t object
170  @param invokeMultiple - invoke_multiple_t object
171  @retval - void or piping result of all delegate callables
172  **/
173  template<class invoke_single_t, class invoke_multiple_t>
174  return_t execute_internal(const invoke_single_t& invokeSingle, const invoke_multiple_t& invokeMultiple)
175  const noexcept;
176 
177 private:
178  /**
179  @brief Add a value using the destruction callback strategy
180  @param key - priority + time key
181  @param value - a function to add
182  @retval - a destruction callback
183  **/
185 
186  /**
187  @brief Add a value using the weak object strategy
188  @tparam object_t - weak object type
189  @tparam callable_t - a callable to execute if the weak is alive
190  @param key - priority + time key
191  @param pWeakObject - weak object to track
192  @param callable - callable_t object
193  **/
194  template<class object_t, callable_c<return_t, object_t*, args_t...> callable_t>
195  void add_weak(time_ordered_priority_key key, std::weak_ptr<object_t> pWeakObject, callable_t callable) noexcept;
196 
197 private:
198  // todo inline allocator
199  container_type m_Functions;
200 
201  // in case a delegate was destroyed before a destruction callback
202  std::shared_ptr<bool> m_pDelegateAliveMarker = std::make_shared<bool>(true);
203 };
204 
205 template<delegate_return_c return_t = void, class... args_t>
206 class delegate;
207 
208 // @copydoc base_delegate
209 template<delegate_return_c return_t, class... args_t>
210  requires(sizeof...(args_t) > 0 && (!std::is_void_v<args_t> && ...))
211 class delegate<return_t, args_t...> final : public base_delegate<delegate<return_t, args_t...>, return_t, args_t...>
212 {
213  using super_type = base_delegate<delegate, return_t, args_t...>;
214 
215 public:
216  /**
217  @brief Execute all the callables the delegate has
218  @param args - arguments to pass to all the callbacks
219  @retval - void or piping result of all delegate callables
220  **/
221  return_t execute(args_t... args) const noexcept;
222 };
223 
224 // @copydoc base_delegate
225 template<delegate_return_c return_t>
226 class delegate<return_t, void> final : public base_delegate<delegate<return_t, void>, return_t>
227 {
229 
230 public:
231  /**
232  @brief Execute all the callables the delegate has
233  @retval - void or piping result of all delegate callables
234  **/
235  return_t execute() const noexcept;
236 };
237 
238 } // namespace qx
239 
240 #include <qx/patterns/delegate.inl>
Single or multicast delegate. Use the qx::delegate<> class in your code.
Definition: delegate.h:54
bool remove(delegate_token_type token) noexcept
Remove a callable using its token.
Definition: delegate.inl:147
return_t execute_internal(const invoke_single_t &invokeSingle, const invoke_multiple_t &invokeMultiple) const noexcept
Execute all callables.
Definition: delegate.inl:160
static derived_t create_singlecast(creation_args_t... args) noexcept
Create a singlecast delegate.
Definition: delegate.inl:62
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:88
void clear() noexcept
Clear all the callables in this delegate.
Definition: delegate.inl:153
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:130
delegate_token_type add_free(callable_t callable, priority ePriority=priority::normal) noexcept
Add a callable without any protection.
Definition: delegate.inl:77
Class for RAII: functor passed in constructor will be called in destructor.
return_t execute(args_t... args) const noexcept
Execute all the callables the delegate has.
priority
User may use the predefined values or the custom ones, for ex. "normal - 1", this type is supposed to...
Definition: priority.h:26
Static assert macros.
A structure that can be used as a key in ordered containers so that items are ordered in descending o...
Definition: priority.h:42