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