qxLib
components.h
Go to the documentation of this file.
1 /**
2 
3  @file components.h
4  @author Khrapov
5  @date 7.03.2021
6  @copyright © Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 #pragma once
10 
11 #include <qx/macros/common.h>
12 #include <qx/priority.h>
13 #include <qx/rtti/rtti.h>
14 #include <qx/rtti/rtti_cast.h>
15 
16 #include <atomic>
17 #include <map>
18 #include <optional>
19 #include <ranges>
20 #include <unordered_map>
21 
22 namespace qx
23 {
24 
25 enum class component_status
26 {
27  default_value = 0,
28  disabled = 1 << 0,
29 };
30 
31 /**
32 
33  @class components
34  @brief Container for components system
35  @details Stores components and allows them to be accessed by a template argument or a class id.
36  Each component has a priority, which is used to iterate through a sequence of objects of a type (final or any super type)
37  or to use getter methods, where a component with the highest priority will be given.
38  You can use qx::priority::disabled to exclude the component from iteration or getters result.
39  @tparam base_component_t - base component type
40  @author Khrapov
41  @date 9.03.2021
42 
43 **/
44 template<std::derived_from<rtti_pure_base> base_component_t>
46 {
47 public:
48  using pointer_type = std::unique_ptr<base_component_t>;
49 
50 private:
51  struct status : time_ordered_priority_key
52  {
53  flags<component_status> statusFlags = component_status::default_value;
54 
55  using time_ordered_priority_key::operator<;
56  constexpr bool operator==(const status&) const noexcept = default;
57  };
58 
59  struct class_data
60  {
61  std::unordered_map<class_id, std::unique_ptr<class_data>> derivedClasses;
62  std::vector<pointer_type> components;
63  std::multimap<status, base_component_t*> priorityCache;
64 
65  [[nodiscard]] class_data& get_or_add_class_data(class_id id) noexcept;
66  };
67 
68  static constexpr auto stub_callback = [](auto&&...)
69  {
70  };
71 
72 public:
73  /**
74  @brief Add a component
75  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
76  @param pComponent - component object
77  @param ePriority - initial component priority
78  @param statusFlags - initial component status
79  @retval - raw component pointer which acts like a component's handle
80  **/
81  template<std::derived_from<base_component_t> component_t>
82  [[maybe_unused]] component_t* add(
83  std::unique_ptr<component_t> pComponent,
84  priority ePriority = priority::normal,
85  flags<component_status> statusFlags = component_status::default_value) noexcept;
86 
87  /**
88  @brief Remove the component from the container
89  @param pRawComponent - raw component pointer which acts like a component's handle
90  @retval - component object
91  **/
92  [[maybe_unused]] std::unique_ptr<base_component_t> remove(const base_component_t* pRawComponent) noexcept;
93 
94  /**
95  @brief Try to get a component of the given type with the highest priority
96  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
97  @param bIncludeDisabled - true if a search should include disabled components
98  @retval - component pointer or nullptr
99  **/
100  template<std::derived_from<base_component_t> component_t>
101  [[nodiscard]] component_t* try_get(bool bIncludeDisabled = false) noexcept;
102 
103  /**
104  @brief Try to get a component of the given type with the highest priority
105  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
106  @param bIncludeDisabled - true if a search should include disabled components
107  @retval - component pointer or nullptr
108  **/
109  template<std::derived_from<base_component_t> component_t>
110  [[nodiscard]] const component_t* try_get(bool bIncludeDisabled = false) const noexcept;
111 
112  /**
113  @brief Try to get a component of the given type id with the highest priority
114  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
115  @param id - type id
116  @param bIncludeDisabled - true if a search should include disabled components
117  @retval - component pointer or nullptr
118  **/
119  template<std::derived_from<base_component_t> component_t = base_component_t>
120  [[nodiscard]] component_t* try_get(class_id id, bool bIncludeDisabled = false) noexcept;
121 
122  /**
123  @brief Try to get a component of the given type id with the highest priority
124  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
125  @param id - type id
126  @param bIncludeDisabled - true if a search should include disabled components
127  @retval - component pointer or nullptr
128  **/
129  template<std::derived_from<base_component_t> component_t = base_component_t>
130  [[nodiscard]] const component_t* try_get(class_id id, bool bIncludeDisabled = false) const noexcept;
131 
132  /**
133  @brief Get a component of the given type with the highest priority (no existence checks are performed)
134  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
135  @param bIncludeDisabled - true if a search should include disabled components
136  @retval - component reference
137  **/
138  template<std::derived_from<base_component_t> component_t>
139  [[nodiscard]] component_t& get(bool bIncludeDisabled = false) noexcept;
140 
141  /**
142  @brief Get a component of the given type with the highest priority (no existence checks are performed)
143  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
144  @param bIncludeDisabled - true if a search should include disabled components
145  @retval - component reference
146  **/
147  template<std::derived_from<base_component_t> component_t>
148  [[nodiscard]] const component_t& get(bool bIncludeDisabled = false) const noexcept;
149 
150  /**
151  @brief Get a component of the given type with the highest priority (no existence checks are performed)
152  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
153  @param id - type id
154  @param bIncludeDisabled - true if a search should include disabled components
155  @retval - component reference
156  **/
157  template<std::derived_from<base_component_t> component_t = base_component_t>
158  [[nodiscard]] base_component_t& get(class_id id, bool bIncludeDisabled = false) noexcept;
159 
160  /**
161  @brief Get a component of the given type with the highest priority (no existence checks are performed)
162  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
163  @param id - type id
164  @param bIncludeDisabled - true if a search should include disabled components
165  @retval - component reference
166  **/
167  template<std::derived_from<base_component_t> component_t = base_component_t>
168  [[nodiscard]] const base_component_t& get(class_id id, bool bIncludeDisabled = false) const noexcept;
169 
170  /**
171  @brief Get a view which may be used in a ranged-based for loop and consists of components of a given type with decreasing non-disabled priority
172  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
173  @retval - view of components
174  **/
175  template<std::derived_from<base_component_t> component_t = base_component_t>
176  [[nodiscard]] auto view() noexcept;
177 
178  /**
179  @brief Get a view which may be used in a ranged-based for loop and consists of components of a given type with decreasing non-disabled priority
180  @tparam component_t - component type which doesn't have to be a final class type, may be any base or super type
181  @retval - view of components
182  **/
183  template<std::derived_from<base_component_t> component_t = base_component_t>
184  [[nodiscard]] auto view() const noexcept;
185 
186  /**
187  @brief Get component status
188  @param pRawComponent - raw component pointer which acts like a component's handle
189  @retval - component status
190  **/
191  [[nodiscard]] std::optional<flags<component_status>> get_component_status(
192  const base_component_t* pRawComponent) const noexcept;
193 
194  /**
195  @brief Set (override) a component status
196  @param pRawComponent - raw component pointer which acts like a component's handle
197  @param newStatus - new component status
198  @retval - true if a component has been found and a new value has been set
199  **/
200  [[maybe_unused]] bool set_component_status(
201  const base_component_t* pRawComponent,
202  flags<component_status> newStatus) noexcept;
203 
204  /**
205  @brief Add new component status flags to the current ones
206  @param pRawComponent - raw component pointer which acts like a component's handle
207  @param newStatuses - new component status flags
208  @retval - true if a component has been found and a new value has been set
209  **/
210  [[maybe_unused]] bool add_component_status(
211  const base_component_t* pRawComponent,
212  flags<component_status> newStatuses) noexcept;
213 
214  /**
215  @brief Add component status flags from the current ones
216  @param pRawComponent - raw component pointer which acts like a component's handle
217  @param statusesToRemove - component status flags to remove
218  @retval - true if a component has been found and a new value has been set
219  **/
220  [[maybe_unused]] bool remove_component_status(
221  const base_component_t* pRawComponent,
222  flags<component_status> statusesToRemove) noexcept;
223 
224  /**
225  @brief Get component priority
226  @param pRawComponent - raw component pointer which acts like a component's handle
227  @retval - component priority
228  **/
229  [[nodiscard]] std::optional<priority> get_component_priority(const base_component_t* pRawComponent) const noexcept;
230 
231  /**
232  @brief Set component priority
233  @param pRawComponent - raw component pointer which acts like a component's handle
234  @param eNewComponentPriority - new component priority
235  @retval - true if a component has been found and a new value has been set
236  **/
237  [[maybe_unused]] bool set_component_priority(
238  const base_component_t* pRawComponent,
239  priority eNewComponentPriority) noexcept;
240 
241  /**
242  @brief Check if container doesn't have any components
243  @retval - true if container doesn't have any components
244  **/
245  [[nodiscard]] bool empty() const noexcept;
246 
247  /**
248  @brief Clear the container, e.g. remove all the components
249  **/
250  void clear() noexcept;
251 
252 private:
253  /**
254  @brief Get a status of a given component
255  @param pRawComponent - raw component pointer which acts like a component's handle
256  @retval - status
257  **/
258  [[nodiscard]] std::optional<status> get_status(const base_component_t* pRawComponent) const noexcept;
259 
260  /**
261  @brief Set a status of a given component
262  @warning This method may change the container, and you shall not use it while iterating over it
263  @param pRawComponent - raw component pointer which acts like a component's handle
264  @param status - new component's status
265  @retval - true if the component is found and a status was set
266  **/
267  [[maybe_unused]] bool set_status(const base_component_t* pRawComponent, status status) noexcept;
268 
269  /**
270  @brief Get or add class data of a given type
271  @tparam component_t - final class type
272  @tparam callable_t - a callable that takes a class data
273  @param iterateClassDataFunction - a function that iterates over a class data from base_component_t + 1 to component_t
274  @retval - or add class data
275  **/
276  template<
277  std::derived_from<base_component_t> component_t,
278  callable_c<void, class_data&> callable_t = decltype(stub_callback)>
279  class_data& get_or_add_class_data(callable_t iterateClassDataFunction = stub_callback) noexcept
280  {
281  /*
282  | | rtti_base | c1 | base_component_t | c2 | c3 | component_t |
283  | t1 | <=======================================================> |
284  | t2 | <===============================> |
285  | t3 | <====================> |
286  */
287  using t1 = typename component_t::inheritance_tuple_type;
288  using t2 = typename base_component_t::inheritance_tuple_type;
289  using t3 = tuple::remove_t<t1, t2>;
290 
291  class_data* pClassData = &m_RootClass;
292  iterateClassDataFunction(*pClassData);
293 
294  tuple::iterate<t3>(
295  [&pClassData, &iterateClassDataFunction]<class T, size_t I>()
296  {
297  pClassData = &pClassData->get_or_add_class_data(T::get_class_id_static());
298  iterateClassDataFunction(*pClassData);
299  });
300 
301  return *pClassData;
302  }
303 
304  /**
305  @brief Get or add class data of a given type
306  @tparam component_t - final class type
307  @tparam callable_t - a callable that takes a class data
308  @param iterateClassDataFunction - a function that iterates over a class data from base_component_t + 1 to component_t
309  @retval - or add class data
310  **/
311  template<
312  std::derived_from<base_component_t> component_t,
313  callable_c<void, class_data&> callable_t = decltype(stub_callback)>
314  const class_data& get_or_add_class_data(callable_t iterateClassDataFunction = stub_callback) const noexcept
315  {
316  return QX_CONST_CAST_THIS()->template get_or_add_class_data<component_t>(std::move(iterateClassDataFunction));
317  }
318 
319  /**
320  @brief Get or add class data of a given type
321  @tparam callable_t - a callable that takes a class data
322  @param pRawComponent - an object used to identify a class type
323  @param iterateClassDataFunction - a function that iterates over a class data from base_component_t + 1 to component_t
324  @retval - or add class data
325  **/
326  template<callable_c<void, class_data&> callable_t = decltype(stub_callback)>
327  class_data& get_or_add_class_data(
328  const base_component_t* pRawComponent,
329  callable_t iterateClassDataFunction = stub_callback) noexcept
330  {
331  class_data* pClassData = &m_RootClass;
332  iterateClassDataFunction(*pClassData);
333 
334  std::span<const class_id> allIds = pRawComponent->get_inheritance_sequence();
335  std::span<const class_id> baseClassIds(
336  std::ranges::find(allIds, base_component_t::get_class_id_static()),
337  allIds.end());
338 
339  for (auto it = baseClassIds.begin() + 1; it != baseClassIds.end(); ++it)
340  {
341  pClassData = &pClassData->get_or_add_class_data(*it);
342  iterateClassDataFunction(*pClassData);
343  }
344 
345  return *pClassData;
346  }
347 
348  /**
349  @brief Get or add class data of a given type
350  @tparam callable_t - a callable that takes a class data
351  @param pRawComponent - an object used to identify a class type
352  @param iterateClassDataFunction - a function that iterates over a class data from base_component_t + 1 to component_t
353  @retval - or add class data
354  **/
355  template<callable_c<void, class_data&> callable_t = decltype(stub_callback)>
356  const class_data& get_or_add_class_data(
357  const base_component_t* pRawComponent,
358  callable_t iterateClassDataFunction = stub_callback) const noexcept
359  {
360  return QX_CONST_CAST_THIS()->get_or_add_class_data(pRawComponent, std::move(iterateClassDataFunction));
361  }
362 
363 private:
364  class_data m_RootClass;
365 };
366 
367 } // namespace qx
368 
Class id, unique for each class using qx rtti system.
Definition: class_id.h:27
Container for components system.
Definition: components.h:46
component_t & get(bool bIncludeDisabled=false) noexcept
Get a component of the given type with the highest priority (no existence checks are performed)
Definition: components.inl:131
std::unique_ptr< base_component_t > remove(const base_component_t *pRawComponent) noexcept
Remove the component from the container.
Definition: components.inl:51
bool add_component_status(const base_component_t *pRawComponent, flags< component_status > newStatuses) noexcept
Add new component status flags to the current ones.
Definition: components.inl:217
bool set_component_priority(const base_component_t *pRawComponent, priority eNewComponentPriority) noexcept
Set component priority.
Definition: components.inl:251
void clear() noexcept
Clear the container, e.g. remove all the components.
Definition: components.inl:270
component_t * try_get(bool bIncludeDisabled=false) noexcept
Try to get a component of the given type with the highest priority.
Definition: components.inl:85
bool set_component_status(const base_component_t *pRawComponent, flags< component_status > newStatus) noexcept
Set (override) a component status.
Definition: components.inl:204
auto view() noexcept
Get a view which may be used in a ranged-based for loop and consists of components of a given type wi...
Definition: components.inl:159
bool empty() const noexcept
Check if container doesn't have any components.
Definition: components.inl:264
bool remove_component_status(const base_component_t *pRawComponent, flags< component_status > statusesToRemove) noexcept
Add component status flags from the current ones.
Definition: components.inl:230
component_t * add(std::unique_ptr< component_t > pComponent, priority ePriority=priority::normal, flags< component_status > statusFlags=component_status::default_value) noexcept
Add a component.
Definition: components.inl:28
std::optional< flags< component_status > > get_component_status(const base_component_t *pRawComponent) const noexcept
Get component status.
Definition: components.inl:196
std::optional< priority > get_component_priority(const base_component_t *pRawComponent) const noexcept
Get component priority.
Definition: components.inl:243
#define QX_CONST_CAST_THIS()
This macro is made for situations where you have a const method and you need exactly the same method ...
Definition: common.h:97
priority
User may use the predefined values or the custom ones, for ex. "normal - 1", this type is supposed to...
Definition: priority.h:26
RTTI system based on polymorphism.
A structure that can be used as a key in ordered containers so that items are ordered in descending o...
Definition: priority.h:42