qxLib
components.inl
Go to the documentation of this file.
1 /**
2 
3  @file components.inl
4  @author Khrapov
5  @date 7.03.2021
6  @copyright (c) Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 namespace details
14 {
15 
16 template<class base_component_t, std::derived_from<base_component_t> component_t>
18 {
19  /*
20  | | rtti_base | c1 | base_component_t | c2 | c3 | component_t |
21  | t1 | <=======================================================> |
22  | t2 | <===============================> |
23  | t3 | <====================> |
24  */
25  using t1 = typename component_t::inheritance_tuple_type;
26  using t2 = typename base_component_t::inheritance_tuple_type;
27  using t3 = tuple_utils::remove_t<t1, t2>;
28 
29  using type = t3;
30 };
31 
32 } // namespace details
33 
34 inline component_status_key::component_status_key(priority ePriority, flags<component_status> eStatusFlags) noexcept
35  : time_ordered_priority_key(ePriority)
36  , m_eStatusFlags(eStatusFlags)
37 {
38 }
39 
41 {
42  return m_eStatusFlags;
43 }
44 
45 // flags<component_status> stores a single int; keep passing it by value.
46 constexpr void component_status_key::set_status_flags(flags<component_status> eStatusFlags) noexcept //-V813
47 {
48  m_eStatusFlags = eStatusFlags;
49 }
50 
51 template<std::derived_from<rtti_pure_base> base_component_t>
53  class_id id) noexcept
54 {
55  if (const auto it = derivedClasses.find(id); it != derivedClasses.end())
56  return *it->second.get();
57 
58  auto pClassData = std::make_unique<class_data>();
59  auto pRawClassData = pClassData.get();
60  derivedClasses[id] = std::move(pClassData);
61  return *pRawClassData;
62 }
63 
64 template<std::derived_from<rtti_pure_base> base_component_t>
65 template<std::derived_from<base_component_t> component_t>
67  std::unique_ptr<component_t> pComponent,
68  priority ePriority,
69  flags<component_status> statusFlags) noexcept
70 {
71  if (!pComponent)
72  return nullptr;
73 
74  auto pRawComponent = pComponent.get();
75  class_data& classData = iterate_class_data(
76  pRawComponent,
77  [ePriority, statusFlags, pRawComponent](class_data& classData)
78  {
79  classData.priorityCache.emplace(component_status_key(ePriority, statusFlags), pRawComponent);
80  });
81  classData.components.push_back(std::move(pComponent));
82  return pRawComponent;
83 }
84 
85 template<std::derived_from<rtti_pure_base> base_component_t>
86 std::unique_ptr<base_component_t> components<base_component_t>::remove(const base_component_t* pRawComponent) noexcept
87 {
88  if (!pRawComponent)
89  return nullptr;
90 
91  class_data& classData = iterate_class_data(
92  pRawComponent,
93  [pRawComponent](class_data& classData)
94  {
95  std::erase_if(
96  classData.priorityCache,
97  [pRawComponent](const auto& pair)
98  {
99  return pair.second == pRawComponent;
100  });
101  });
102 
103  const auto it = std::ranges::find_if(
104  classData.components,
105  [pRawComponent](const pointer_type& pointer)
106  {
107  return pointer.get() == pRawComponent;
108  });
109 
110  if (it == classData.components.end())
111  return nullptr;
112 
113  std::unique_ptr<base_component_t> pComponent = std::move(*it);
114  classData.components.erase(it);
115  return pComponent;
116 }
117 
118 template<std::derived_from<rtti_pure_base> base_component_t>
119 template<std::derived_from<base_component_t> component_t>
120 component_t* components<base_component_t>::try_get(bool bIncludeDisabled) noexcept
121 {
122  class_data& classData = get_or_add_class_data<component_t>();
123  const auto it = std::ranges::find_if(
124  classData.priorityCache,
125  [bIncludeDisabled](const auto& pair)
126  {
127  return bIncludeDisabled || !pair.first.get_status_flags().contains(component_status::disabled);
128  });
129 
130  return it != classData.priorityCache.end() ? static_cast<component_t*>(it->second) : nullptr;
131 }
132 
133 template<std::derived_from<rtti_pure_base> base_component_t>
134 template<std::derived_from<base_component_t> component_t>
135 const component_t* components<base_component_t>::try_get(bool bIncludeDisabled) const noexcept
136 {
137  return QX_CONST_CAST_THIS()->template try_get<component_t>(bIncludeDisabled);
138 }
139 
140 template<std::derived_from<rtti_pure_base> base_component_t>
141 template<std::derived_from<base_component_t> component_t>
142 component_t* components<base_component_t>::try_get(class_id id, bool bIncludeDisabled) noexcept
143 {
144  // we could improve it from O(N) to O(log(N)) if we could use id->is_derived_from(id) without an object itself
145  // we will do it when a reflection system is fully implemented
146  const auto it = std::ranges::find_if(
147  m_RootClass.priorityCache,
148  [id, bIncludeDisabled](const auto& pair)
149  {
150  return (bIncludeDisabled || !pair.first.get_status_flags().contains(component_status::disabled))
151  && pair.second->is_derived_from_id(id);
152  });
153 
154  return it != m_RootClass.priorityCache.end() ? rtti_cast<component_t>(it->second) : nullptr;
155 }
156 
157 template<std::derived_from<rtti_pure_base> base_component_t>
158 template<std::derived_from<base_component_t> component_t>
159 const component_t* components<base_component_t>::try_get(class_id id, bool bIncludeDisabled) const noexcept
160 {
161  return QX_CONST_CAST_THIS()->template try_get<component_t>(id, bIncludeDisabled);
162 }
163 
164 template<std::derived_from<rtti_pure_base> base_component_t>
165 template<std::derived_from<base_component_t> component_t>
166 component_t& components<base_component_t>::get(bool bIncludeDisabled) noexcept
167 {
168  return *try_get<component_t>(bIncludeDisabled);
169 }
170 
171 template<std::derived_from<rtti_pure_base> base_component_t>
172 template<std::derived_from<base_component_t> component_t>
173 const component_t& components<base_component_t>::get(bool bIncludeDisabled) const noexcept
174 {
175  return QX_CONST_CAST_THIS()->template get<component_t>(bIncludeDisabled);
176 }
177 
178 template<std::derived_from<rtti_pure_base> base_component_t>
179 template<std::derived_from<base_component_t> component_t>
180 base_component_t& components<base_component_t>::get(class_id id, bool bIncludeDisabled) noexcept
181 {
182  return *try_get<component_t>(id, bIncludeDisabled);
183 }
184 
185 template<std::derived_from<rtti_pure_base> base_component_t>
186 template<std::derived_from<base_component_t> component_t>
187 const base_component_t& components<base_component_t>::get(class_id id, bool bIncludeDisabled) const noexcept
188 {
189  return QX_CONST_CAST_THIS()->template get<component_t>(id, bIncludeDisabled);
190 }
191 
192 template<std::derived_from<rtti_pure_base> base_component_t>
193 template<std::derived_from<base_component_t> component_t>
194 auto components<base_component_t>::view(bool bIncludeDisabled) noexcept
195 {
196  class_data& classData = get_or_add_class_data<component_t>();
197  return classData.priorityCache
198  | std::views::filter(
199  [bIncludeDisabled](const auto& pair)
200  {
201  return pair.second->template is_derived_from<component_t>()
202  && (bIncludeDisabled || !pair.first.get_status_flags().contains(component_status::disabled));
203  })
204  | std::views::transform(
205  [](auto& pair) -> component_t&
206  {
207  return *static_cast<component_t*>(pair.second);
208  });
209 }
210 
211 template<std::derived_from<rtti_pure_base> base_component_t>
212 template<std::derived_from<base_component_t> component_t>
213 auto components<base_component_t>::view(bool bIncludeDisabled) const noexcept
214 {
215  const class_data& classData = get_or_add_class_data<component_t>();
216  return classData.priorityCache
217  | std::views::filter(
218  [bIncludeDisabled](const auto& pair)
219  {
220  return pair.second->template is_derived_from<component_t>()
221  && (bIncludeDisabled || !pair.first.get_status_flags().contains(component_status::disabled));
222  })
223  | std::views::transform(
224  [](const auto& pair) -> const component_t&
225  {
226  return *static_cast<const component_t*>(pair.second);
227  });
228 }
229 
230 template<std::derived_from<rtti_pure_base> base_component_t>
231 std::optional<flags<component_status>> components<base_component_t>::get_component_status(
232  const base_component_t* pRawComponent) const noexcept
233 {
234  std::optional<component_status_key> optComponentStatus = get_status(pRawComponent);
235  return optComponentStatus ? std::optional(optComponentStatus->get_status_flags()) : std::nullopt;
236 }
237 
238 template<std::derived_from<rtti_pure_base> base_component_t>
240  const base_component_t* pRawComponent,
241  flags<component_status> newStatus) noexcept
242 {
243  std::optional<component_status_key> optComponentStatus = get_status(pRawComponent);
244  if (!optComponentStatus)
245  return false;
246 
247  optComponentStatus->set_status_flags(newStatus);
248  return set_status(pRawComponent, *optComponentStatus);
249 }
250 
251 template<std::derived_from<rtti_pure_base> base_component_t>
253  const base_component_t* pRawComponent,
254  flags<component_status> newStatuses) noexcept
255 {
256  std::optional<flags<component_status>> optComponentStatus = get_component_status(pRawComponent);
257  if (!optComponentStatus)
258  return false;
259 
260  optComponentStatus->add(newStatuses);
261  return set_component_status(pRawComponent, *optComponentStatus);
262 }
263 
264 template<std::derived_from<rtti_pure_base> base_component_t>
266  const base_component_t* pRawComponent,
267  flags<component_status> statusesToRemove) noexcept
268 {
269  std::optional<flags<component_status>> optComponentStatus = get_component_status(pRawComponent);
270  if (!optComponentStatus)
271  return false;
272 
273  optComponentStatus->remove(statusesToRemove);
274  return set_component_status(pRawComponent, *optComponentStatus);
275 }
276 
277 template<std::derived_from<rtti_pure_base> base_component_t>
279  const base_component_t* pRawComponent) const noexcept
280 {
281  std::optional<component_status_key> optComponentStatus = get_status(pRawComponent);
282  return optComponentStatus ? std::optional(optComponentStatus->get_priority()) : std::nullopt;
283 }
284 
285 template<std::derived_from<rtti_pure_base> base_component_t>
287  const base_component_t* pRawComponent,
288  priority eNewComponentPriority) noexcept
289 {
290  std::optional<component_status_key> optComponentStatus = get_status(pRawComponent);
291  if (!optComponentStatus)
292  return false;
293 
294  optComponentStatus->set_priority(eNewComponentPriority);
295  return set_status(pRawComponent, *optComponentStatus);
296 }
297 
298 template<std::derived_from<rtti_pure_base> base_component_t>
300 {
301  return m_RootClass.priorityCache.empty();
302 }
303 
304 template<std::derived_from<rtti_pure_base> base_component_t>
306 {
307  m_RootClass.derivedClasses.clear();
308  m_RootClass.components.clear();
309  m_RootClass.priorityCache.clear();
310 }
311 
312 template<std::derived_from<rtti_pure_base> base_component_t>
313 std::optional<component_status_key> components<base_component_t>::get_status(
314  const base_component_t* pRawComponent) const noexcept
315 {
316  if (!pRawComponent)
317  return std::nullopt;
318 
319  auto it = std::ranges::find_if(
320  m_RootClass.priorityCache,
321  [pRawComponent](const auto& pair)
322  {
323  return pair.second == pRawComponent;
324  });
325 
326  return it != m_RootClass.priorityCache.end() ? std::optional<component_status_key>(it->first) : std::nullopt;
327 }
328 
329 
330 template<std::derived_from<rtti_pure_base> base_component_t>
331 bool components<base_component_t>::set_status(
332  const base_component_t* pRawComponent,
333  component_status_key status) noexcept
334 {
335  if (!pRawComponent)
336  return false;
337 
338  bool bChanged = true;
339  iterate_class_data(
340  pRawComponent,
341  [pRawComponent, status, &bChanged](class_data& classData)
342  {
343  const auto it = std::ranges::find_if(
344  classData.priorityCache,
345  [pRawComponent](const auto& pair)
346  {
347  return pair.second == pRawComponent;
348  });
349 
350  if (it == classData.priorityCache.end())
351  {
352  bChanged = false;
353  return;
354  }
355 
356  if (it->first != status)
357  {
358  classData.priorityCache.emplace(status, it->second);
359  classData.priorityCache.erase(it);
360  }
361  });
362 
363  return bChanged;
364 }
365 
366 template<std::derived_from<rtti_pure_base> base_component_t>
367 template<
368  std::derived_from<base_component_t> component_t,
369  callable_c<void, typename components<base_component_t>::class_data&> callable_t>
370 typename components<base_component_t>::class_data& components<base_component_t>::iterate_class_data(
371  callable_t iterateClassDataFunction) noexcept
372 {
373  class_data* pClassData = &m_RootClass;
374  iterateClassDataFunction(*pClassData);
375 
376  tuple_utils::iterate<typename details::get_inheritance_sequence<base_component_t, component_t>::type>(
377  [&pClassData, &iterateClassDataFunction]<class T, size_t I>()
378  {
379  pClassData = &pClassData->get_or_add_class_data(T::get_class_id_static());
380  iterateClassDataFunction(*pClassData);
381  });
382 
383  return *pClassData;
384 }
385 
386 template<std::derived_from<rtti_pure_base> base_component_t>
387 template<
388  std::derived_from<base_component_t> component_t,
389  callable_c<void, typename components<base_component_t>::class_data&> callable_t>
390 const typename components<base_component_t>::class_data& components<base_component_t>::iterate_class_data(
391  callable_t iterateClassDataFunction) const noexcept
392 {
393  return QX_CONST_CAST_THIS()->template iterate_class_data<component_t>(std::move(iterateClassDataFunction));
394 }
395 
396 template<std::derived_from<rtti_pure_base> base_component_t>
397 template<std::derived_from<base_component_t> component_t>
398 typename components<base_component_t>::class_data& components<base_component_t>::get_or_add_class_data() noexcept
399 {
400  class_data* pClassData = &m_RootClass;
401 
402  tuple_utils::iterate<typename details::get_inheritance_sequence<base_component_t, component_t>::type>(
403  [&pClassData]<class T, size_t I>()
404  {
405  pClassData = &pClassData->get_or_add_class_data(T::get_class_id_static());
406  });
407 
408  return *pClassData;
409 }
410 
411 template<std::derived_from<rtti_pure_base> base_component_t>
412 template<std::derived_from<base_component_t> component_t>
413 const typename components<base_component_t>::class_data& components<base_component_t>::get_or_add_class_data()
414  const noexcept
415 {
416  return QX_CONST_CAST_THIS()->template get_or_add_class_data<component_t>();
417 }
418 
419 template<std::derived_from<rtti_pure_base> base_component_t>
420 template<callable_c<void, typename components<base_component_t>::class_data&> callable_t>
421 typename components<base_component_t>::class_data& components<base_component_t>::iterate_class_data(
422  const base_component_t* pRawComponent,
423  callable_t iterateClassDataFunction) noexcept
424 {
425  class_data* pClassData = &m_RootClass;
426  iterateClassDataFunction(*pClassData);
427 
428  std::span<const class_id> allIds = pRawComponent->get_inheritance_sequence();
429  std::span<const class_id> baseClassIds(
430  std::ranges::find(allIds, base_component_t::get_class_id_static()),
431  allIds.end());
432 
433  for (auto it = baseClassIds.begin() + 1; it != baseClassIds.end(); ++it)
434  {
435  pClassData = &pClassData->get_or_add_class_data(*it);
436  iterateClassDataFunction(*pClassData);
437  }
438 
439  return *pClassData;
440 }
441 
442 template<std::derived_from<rtti_pure_base> base_component_t>
443 template<callable_c<void, typename components<base_component_t>::class_data&> callable_t>
444 const typename components<base_component_t>::class_data& components<base_component_t>::iterate_class_data(
445  const base_component_t* pRawComponent,
446  callable_t iterateClassDataFunction) const noexcept
447 {
448  return QX_CONST_CAST_THIS()->iterate_class_data(pRawComponent, std::move(iterateClassDataFunction));
449 }
450 
451 template<std::derived_from<rtti_pure_base> base_component_t>
452 typename components<base_component_t>::class_data& components<base_component_t>::get_or_add_class_data(
453  const base_component_t* pRawComponent) noexcept
454 {
455  class_data* pClassData = &m_RootClass;
456 
457  std::span<const class_id> allIds = pRawComponent->get_inheritance_sequence();
458  std::span<const class_id> baseClassIds(
459  std::ranges::find(allIds, base_component_t::get_class_id_static()),
460  allIds.end());
461 
462  for (auto it = baseClassIds.begin() + 1; it != baseClassIds.end(); ++it)
463  pClassData = &pClassData->get_or_add_class_data(*it);
464 
465  return *pClassData;
466 }
467 
468 template<std::derived_from<rtti_pure_base> base_component_t>
469 const typename components<base_component_t>::class_data& components<base_component_t>::get_or_add_class_data(
470  const base_component_t* pRawComponent) const noexcept
471 {
472  return QX_CONST_CAST_THIS()->get_or_add_class_data(pRawComponent);
473 }
474 
475 } // namespace qx
Class id, unique for each class using qx rtti system.
Definition: class_id.h:27
A class that acts like time_ordered_priority_key but has a status field.
Definition: components.h:40
constexpr void set_status_flags(flags< component_status > eStatusFlags) noexcept
Set object status.
Definition: components.inl:46
constexpr flags< component_status > get_status_flags() const noexcept
Get object status.
Definition: components.inl:40
Container for components system.
Definition: components.h:85
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:166
std::unique_ptr< base_component_t > remove(const base_component_t *pRawComponent) noexcept
Remove the component from the container.
Definition: components.inl:86
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:252
bool set_component_priority(const base_component_t *pRawComponent, priority eNewComponentPriority) noexcept
Set component priority.
Definition: components.inl:286
void clear() noexcept
Clear the container, e.g. remove all the components.
Definition: components.inl:305
component_t * try_get(bool bIncludeDisabled=false) noexcept
Try to get a component of the given type with the highest priority.
Definition: components.inl:120
bool set_component_status(const base_component_t *pRawComponent, flags< component_status > newStatus) noexcept
Set (override) a component status.
Definition: components.inl:239
bool empty() const noexcept
Check if container doesn't have any components.
Definition: components.inl:299
auto view(bool bIncludeDisabled=false) 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:194
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:265
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:66
std::optional< flags< component_status > > get_component_status(const base_component_t *pRawComponent) const noexcept
Get component status.
Definition: components.inl:231
std::optional< priority > get_component_priority(const base_component_t *pRawComponent) const noexcept
Get component priority.
Definition: components.inl:278
A class that can be used as a key in ordered containers so that items are ordered in descending order...
Definition: priority.h:47
#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:63
priority
User may use the predefined values or the custom ones, for ex. "normal - 1", this type is supposed to...
Definition: priority.h:27