MCRO
C++23 utilities for Unreal Engine.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
Composition.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12#pragma once
13
14#include "CoreMinimal.h"
15#include "Mcro/Any.h"
16#include "Mcro/TextMacros.h"
17#include "Mcro/AssertMacros.h"
18#include "Mcro/Range.h"
19#include "Mcro/Range/Views.h"
21
22/** @brief Namespace containing utilities and base classes for type composition */
24{
25 using namespace Mcro::Any;
26 using namespace Mcro::Range;
27
28 class IComposable;
29
30 /**
31 * @brief
32 * Inherit from this empty interface to signal that the inheriting class knows that it's a component and that it
33 * can receive info about the composable class it is being registered to.
34 *
35 * Define `OnComponentRegistered(T& to)` in the inheriting class, where T is the expected type of the
36 * composable parent. In case this component is registered to a composable class which is not convertible to T
37 * then `OnComponentRegistered` will be silently ignored.
38 */
40 {
41 // void OnComponentRegistered(T& to) {}
42 };
43
44 /**
45 * @brief
46 * Inherit from this empty interface to signal that the inheriting class knows that it's a component and that it
47 * can receive info about the composable class it is being registered to.
48 *
49 * Define `OnComponentRegistered(T& to)` in the inheriting class, where T is the expected type of the
50 * composable parent. In case of `IStrictComponent`, it is a compile error to register this class to a composable
51 * class which is not convertible to T.
52 */
54 {
55 // void OnComponentRegistered(T& to) {}
56 };
57
58 template <typename T>
59 concept CComposable = CDerivedFrom<T, IComposable>;
60
61 template <typename T>
62 concept CExplicitComponent = CDerivedFrom<T, IComponent>;
63
64 template <typename T>
65 concept CStrictComponent = CDerivedFrom<T, IStrictComponent>;
66
67 template <typename T, typename Composition>
69 && requires(std::decay_t<T>& t, Composition&& parent)
70 {
71 t.OnComponentRegistered(parent);
72 }
73 ;
74
75 template <typename T, typename Composition>
77
78 template <typename T, typename Composition>
80
81 /**
82 * @brief A base class which can bring type based class-composition to a derived class
83 *
84 * This exists because indeed Unreal has its own composition model (actors / actor-components) or it has the
85 * subsystem architecture for non-actors, they still require to be used with UObjects. `IComposable` allows
86 * any C++ objects to have type-safe runtime managed optional components which can be configured separately for
87 * each instance.
88 *
89 * The only requirement for components is that they need to be copy and move constructible (as is the default with
90 * plain-old-C++ objects, if they don't have members where either constructors are deleted or inaccessible). This
91 * limitation is imposed by `FAny` only for easier interfacing, but the API for managing components will never move
92 * or copy them by itself.
93 * The composable class doesn't have that limitation.
94 *
95 * Usage:
96 * @code
97 * //// Given the following types we want to use as components:
98 *
99 * struct FSimpleComponent { int A = 0; };
100 *
101 * struct IBaseComponent { int B; };
102 *
103 * // | This allows us not repeating ourselves, more on this later
104 * // V
105 * struct FComponentImplementation : TInherit<IBaseComponent>
106 * {
107 * FComponentImplementation(int b, int c) : B(b), C(c) {}
108 * int C;
109 * };
110 *
111 * struct IRegularBase { int D; };
112 *
113 * // | We have to repeat this if we want to get components with this base class
114 * // V
115 * struct FRegularInherited : IRegularBase
116 * {
117 * FRegularInherited(int d, int e) : D(d), E(e) {}
118 * int E;
119 * };
120 *
121 * //// Given the following composable type:
122 *
123 * class FMyComposableType : public IComposable {};
124 *
125 * //// Declare their composition at construction:
126 *
127 * auto MyStuff = FMyComposableType()
128 * .WithComponent<FSimpleComponent>() // <- Simply add components with their types
129 * .WithComponent(new FComponentImplementation(1, 2)) // <- Or simply use new operator
130 * // (IComposable assumes ownership)
131 * // Because FComponentImplementation uses TInherit
132 * // IBaseComponent is not needed to be repeated here
133 * .WithComponent(new FRegularInherited(3, 4)) //
134 * .WithAlias<IRegularBase>() // <- FRegularInherited cannot tell automatically that it
135 * // inherits from IRegularBase so we need to specify
136 * // that here explicitly in case we want to get
137 * // FRegularInherited component via its base class
138 * ;
139 *
140 * //// Get components at runtime:
141 *
142 * int a = MyStuff.GetComponent<FSimpleComponent>().A; //
143 * // -> 0 //
144 * int b = MyStuff.GetComponent<IBaseComponent>().B; // <- We can get the component via base class here, only
145 * // -> 1 // because it was exposed via TInherit
146 * int d = MyStuff.GetComponent<IRegularBase>().D; // <- We can get the component via base class here, because
147 * // -> 3 // we explicitly specified it during registration
148 * FVector* v = MyStuff.TryGetComponent<FVector>(); // <- If there's any doubt that a component may not have
149 * // -> nullptr; FVector wasn't added as a component // been registered, use TryGetComponent instead.
150 * @endcode
151 *
152 * As mentioned earlier, components are not required to have any arbitrary type traits, but if they inherit from
153 * `IComponent` or `IStrictComponent` they can receive extra information when they're registered for a composable
154 * class. The difference between the two is that `IComponent` doesn't mind if it's attached to a composable class
155 * it doesn't know about, however it is a compile error if an `IStrictComponent` is attempted to be attached to
156 * an incompatible class.
157 *
158 * For example
159 * @code
160 * //// Given the following types we want to use as components:
161 *
162 * struct FChillComponent : IComponent
163 * {
164 * void OnComponentRegistered(FExpectedParent& to) {}
165 * };
166 *
167 * struct FStrictComponent : IStrictComponent
168 * {
169 * void OnComponentRegistered(FExpectedParent& to) {}
170 * };
171 *
172 * //// Given the following composable types:
173 *
174 * class FExpectedParent : public IComposable {};
175 * class FSomeOtherParent : public IComposable {};
176 *
177 * //// Declare their composition at construction:
178 *
179 * auto MyOtherStuff = FExpectedParent()
180 * .WithComponent<FChillComponent>() // OK, and OnComponentRegistered is called
181 * .WithComponent<FStrictComponent>() // OK, and OnComponentRegistered is called
182 *
183 * auto MyStuff = FSomeOtherParent()
184 * .WithComponent<FChillComponent>() // OK, but OnComponentRegistered won't be called.
185 *
186 * .WithComponent<FStrictComponent>() // COMPILE ERROR, CCompatibleComponent concept is not satisfied
187 * // because FSomeOtherParent is not convertible to FExpectedParent
188 * // at OnComponentRegistered(FExpectedParent& to)
189 * ;
190 * @endcode
191 *
192 * Explicit components can explicitly support multiple composable classes via function overloading or templating
193 * (with deduced type parameters).
194 *
195 * If a component type uses `TInherit` template or has a `using Bases = TTypes<...>` member alias in a similar way:
196 * @code
197 * class FMyComponent : public TInherit<IFoo, IBar, IEtc>
198 * {
199 * // ...
200 * }
201 * @endcode
202 * Then the specified base classes will be automatically registered as component aliases. When this is used for
203 * explicit components, `IComponent` or `IStrictComponent` is strongly discouraged to be used in `TInherit`'s
204 * parameter pack. So declare inheritance the following way:
205 *
206 * @code
207 * class FMyComponent
208 * : public TInherit<IFoo, IBar, IEtc>
209 * , public IComponent
210 * {
211 * // ...
212 * }
213 * @endcode
214 *
215 * @todo
216 * C++ 26 has promising proposal for static value-based reflection, which can gather metadata from classes
217 * or even emit them. The best summary I found so far is a stack-overflow answer https://stackoverflow.com/a/77477029
218 * Once that's available we can gather base classes in compile time, and do dynamic casting of objects without
219 * the need for intrusive extra syntax, or extra work at construction.
220 * Currently GCC's `__bases` would be perfect for the job, but other popular compilers don't have similar
221 * intrinsics. Once such a feature becomes widely available base classes can be automatically added as aliases for
222 * registered components.
223 */
224 class MCRO_API IComposable
225 {
226 uint64 LastAddedComponentHash = 0;
227 mutable TMap<uint64, FAny> Components;
228 mutable TMap<uint64, TArray<uint64>> ComponentAliases;
229
230 bool HasExactComponent(uint64 typeHash) const;
231 bool HasComponentAliasUnchecked(uint64 typeHash) const;
232 bool HasComponentAlias(uint64 typeHash) const;
233 void AddComponentAlias(uint64 mainType, uint64 validAs);
234
235 template <typename ValidAs>
236 void AddComponentAlias(uint64 mainType)
237 {
238 Components[mainType].WithAlias<ValidAs>();
239 AddComponentAlias(mainType, TTypeHash<ValidAs>);
240
241 if constexpr (CHasBases<ValidAs>)
242 {
244 {
245 AddComponentAlias(mainType, TTypeHash<Base>);
246 });
247 }
248 }
249
250 ranges::any_view<FAny*> GetExactComponent(uint64 typeHash) const;
251 ranges::any_view<FAny*> GetAliasedComponents(uint64 typeHash) const;
252 ranges::any_view<FAny*> GetComponentsPrivate(uint64 typeHash) const;
253
254 public:
255
256 /**
257 * @brief
258 * Add a component to this composable class.
259 *
260 * @tparam MainType The exact component type (deduced from `newComponent`
261 * @tparam Self Deducing this
262 * @param self Deducing this
263 *
264 * @param newComponent
265 * A pointer to the new component being added. `IComposable` will assume ownership of the new component
266 * adhering to RAII. Make sure the lifespan of the provided object is not managed by something else or the
267 * stack, in fact better to stick with the `new` operator.
268 *
269 * @param facilities
270 * Customization point for object copy/move and delete methods. See `TAnyTypeFacilities`
271 */
272 template <typename MainType, typename Self>
274 void AddComponent(this Self&& self, MainType* newComponent, TAnyTypeFacilities<MainType> const& facilities = {})
275 {
276 ASSERT_CRASH(newComponent);
277 ASSERT_CRASH(!self.HasExactComponent(TTypeHash<MainType>),
278 ->WithMessageF(
279 TEXT_"{0} cannot be added because another component already exists under that type.",
280 TTypeName<MainType>
281 )
282 ->WithDetailsF(TEXT_
283 "Try wrapping your component in an empty derived type, and register it with its base type {0} as its"
284 " alias. Later on both the current and the already existing component can be accessed via"
285 " `GetComponents<{0}>()` which returns a range of all matching components.",
286 TTypeName<MainType>
287 )
288 );
289
290 self.Components.Add(TTypeHash<MainType>, FAny(newComponent, facilities));
291 self.LastAddedComponentHash = TTypeHash<MainType>;
292
293 if constexpr (CHasBases<MainType>)
294 {
296 {
297 // FAny also deals with CHasBases so we can skip explicitly registering them here
298 self.AddComponentAlias(TTypeHash<MainType>, TTypeHash<Base>);
299 });
300 }
301
302 if constexpr (CCompatibleExplicitComponent<MainType, Self>)
303 newComponent->OnComponentRegistered(self);
304 }
305
306 /**
307 * @brief
308 * Add a default constructed component to this composable class.
309 *
310 * @tparam MainType The exact component type
311 * @tparam Self Deducing this
312 * @param self Deducing this
313 *
314 * @param facilities
315 * Customization point for object copy/move and delete methods. See `TAnyTypeFacilities`
316 */
317 template <CDefaultInitializable MainType, typename Self>
318 requires CCompatibleComponent<MainType, Self>
319 void AddComponent(this Self&& self, TAnyTypeFacilities<MainType> const& facilities = {})
320 {
321 Forward<Self>(self).template AddComponent<MainType, Self>(new MainType(), facilities);
322 }
323
324 /**
325 * @brief
326 * Add a list of types the last added component is convertible to and may be used to get the last component
327 * among others which may list the same aliases.
328 *
329 * @warning
330 * Calling this function before adding a component may result in a runtime crash!
331 *
332 * @tparam ValidAs
333 * The list of other types the last added component is convertible to and may be used to get the last component
334 * among others which may list the same aliases.
335 */
336 template <typename... ValidAs>
337 void AddAlias()
338 {
339 ASSERT_CRASH(LastAddedComponentHash != 0 && Components.Contains(LastAddedComponentHash),
340 ->WithMessage(TEXT_"Component aliases were listed, but no components were added before.")
341 ->WithDetails(TEXT_"Make sure `AddAlias` or `WithAlias` is called after `AddComponent` / `WithComponent`.")
342 );
343 (AddComponentAlias<ValidAs>(LastAddedComponentHash), ...);
344 }
345
346 /**
347 * @brief
348 * Add a component to this composable class with a fluent API.
349 *
350 * This overload is available for composable classes which also inherit from `TSharedFromThis`.
351 *
352 * @tparam MainType The exact component type (deduced from `newComponent`
353 * @tparam Self Deducing this
354 * @param self Deducing this
355 *
356 * @param newComponent
357 * A pointer to the new component being added. `IComposable` will assume ownership of the new component
358 * adhering to RAII. Make sure the lifespan of the provided object is not managed by something else or the
359 * stack, in fact better to stick with the `new` operator.
360 *
361 * @param facilities
362 * Customization point for object copy/move and delete methods. See `TAnyTypeFacilities`
363 *
364 * @return
365 * If the composable class also inherits from `TSharedFromThis` return a shared ref.
366 */
367 template <typename MainType, CSharedFromThis Self>
369 auto WithComponent(this Self&& self, MainType* newComponent, TAnyTypeFacilities<MainType> const& facilities = {})
370 {
371 Forward<Self>(self).template AddComponent<MainType, Self>(newComponent, facilities);
372 return StaticCastSharedRef<std::decay_t<Self>>(self.AsShared());
373 }
374
375 /**
376 * @brief
377 * Add a default constructed component to this composable class with a fluent API.
378 *
379 * This overload is available for composable classes which also inherit from `TSharedFromThis`.
380 *
381 * @tparam MainType The exact component type
382 * @tparam Self Deducing this
383 * @param self Deducing this
384 *
385 * @param facilities
386 * Customization point for object copy/move and delete methods. See `TAnyTypeFacilities`
387 *
388 * @return
389 * If the composable class also inherits from `TSharedFromThis` return a shared ref.
390 */
391 template <CDefaultInitializable MainType, CSharedFromThis Self>
392 requires CCompatibleComponent<MainType, Self>
393 auto WithComponent(this Self&& self, TAnyTypeFacilities<MainType> const& facilities = {})
394 {
395 Forward<Self>(self).template AddComponent<MainType, Self>(facilities);
396 return StaticCastSharedRef<std::decay_t<Self>>(self.AsShared());
397 }
398
399 /**
400 * @brief
401 * Add a component to this composable class with a fluent API.
402 *
403 * This overload is available for composable classes which are not explicitly meant to be used with shared pointers.
404 *
405 * @tparam MainType The exact component type (deduced from `newComponent`
406 * @tparam Self Deducing this
407 * @param self Deducing this
408 *
409 * @param newComponent
410 * A pointer to the new component being added. `IComposable` will assume ownership of the new component
411 * adhering to RAII. Make sure the lifespan of the provided object is not managed by something else or the
412 * stack, in fact better to stick with the `new` operator.
413 *
414 * @param facilities
415 * Customization point for object copy/move and delete methods. See `TAnyTypeFacilities`
416 *
417 * @return
418 * Perfect-forwarded self.
419 */
420 template <typename MainType, typename Self>
421 requires (CCompatibleComponent<MainType, Self> && !CSharedFromThis<Self>)
422 decltype(auto) WithComponent(this Self&& self, MainType* newComponent, TAnyTypeFacilities<MainType> const& facilities = {})
423 {
424 Forward<Self>(self).template AddComponent<MainType, Self>(newComponent, facilities);
425 return Forward<Self>(self);
426 }
427
428 /**
429 * @brief
430 * Add a default constructed component to this composable class with a fluent API.
431 *
432 * This overload is available for composable classes which are not explicitly meant to be used with shared pointers.
433 *
434 * @tparam MainType The exact component type
435 * @tparam Self Deducing this
436 * @param self Deducing this
437 *
438 * @param facilities
439 * Customization point for object copy/move and delete methods. See `TAnyTypeFacilities`
440 *
441 * @return
442 * Perfect-forwarded self.
443 */
444 template <CDefaultInitializable MainType, typename Self>
445 requires (CCompatibleComponent<MainType, Self> && !CSharedFromThis<Self>)
446 decltype(auto) WithComponent(this Self&& self, TAnyTypeFacilities<MainType> const& facilities = {})
447 {
448 Forward<Self>(self).template AddComponent<MainType, Self>(facilities);
449 return Forward<Self>(self);
450 }
451
452 /**
453 * @brief
454 * Add a type, the last added component is convertible to and may be used to get the last component among
455 * others which may list the same aliases.
456 *
457 * Only one alias may be specified this way because of templating syntax intricacies, but it may be called
458 * multiple times in a sequence to add multiple aliases for the same component.
459 *
460 * Usage:
461 * @code
462 * auto result = IComposable()
463 * .WithComponent<FMyComponent>()
464 * .WithAlias<FMyComponentBase>()
465 * .WithAlias<IMyComponentInterface>()
466 * ;
467 * @endcode
468 *
469 * For declaring multiple aliases in one go, use `With(TTypes<...>)` member template method.
470 *
471 * This overload is available for composable classes which also inherit from `TSharedFromThis`.
472 *
473 * @warning
474 * Calling this function before adding a component may result in a runtime crash!
475 *
476 * @tparam ValidAs
477 * A type, the last added component is convertible to and may be used to get the last component among others
478 * which may list the same aliases.
479 *
480 * @tparam Self Deducing this
481 * @param self Deducing this
482 *
483 * @return
484 * If the composable class also inherits from `TSharedFromThis` return a shared ref.
485 */
486 template <typename ValidAs, CSharedFromThis Self>
487 auto WithAlias(this Self&& self)
488 {
489 Forward<Self>(self).template AddAlias<ValidAs>();
490 return StaticCastSharedRef<std::decay_t<Self>>(self.AsShared());
491 }
492
493 /**
494 * @brief
495 * Add a type, the last added component is convertible to and may be used to get the last component among
496 * others which may list the same aliases.
497 *
498 * Only one alias may be specified this way because of templating syntax intricacies, but it may be called
499 * multiple times in a sequence to add multiple aliases for the same component.
500 *
501 * Usage:
502 * @code
503 * auto result = IComposable()
504 * .WithComponent<FMyComponent>()
505 * .WithAlias<FMyComponentBase>()
506 * .WithAlias<IMyComponentInterface>()
507 * ;
508 * @endcode
509 *
510 * For declaring multiple aliases in one go, use `With(TTypes<...>)` member template method.
511 *
512 * This overload is available for composable classes which are not explicitly meant to be used with shared pointers.
513 *
514 * @warning
515 * Calling this function before adding a component may result in a runtime crash!
516 *
517 * @tparam ValidAs
518 * A type, the last added component is convertible to and may be used to get the last component among others
519 * which may list the same aliases.
520 *
521 * @tparam Self Deducing this
522 * @param self Deducing this
523 *
524 * @return
525 * Perfect-forwarded self.
526 */
527 template <typename ValidAs, typename Self>
528 requires (!CSharedFromThis<Self>)
529 decltype(auto) WithAlias(this Self&& self)
530 {
531 Forward<Self>(self).template AddAlias<ValidAs>();
532 return Forward<Self>(self);
533 }
534
535 /**
536 * @brief
537 * Add a list of types the last added component is convertible to and may be used to get the last component
538 * among others which may list the same aliases.
539 *
540 * Usage:
541 * @code
542 * auto result = IComposable()
543 * .WithComponent<FMyComponent>().With(TAlias<
544 * FMyComponentBase,
545 * IMyComponentInterface
546 * >)
547 * ;
548 * @endcode
549 *
550 * This overload is available for composable classes which also inherit from `TSharedFromThis`.
551 *
552 * @warning
553 * Calling this function before adding a component may result in a runtime crash!
554 *
555 * @tparam ValidAs
556 * The list of other types the last added component is convertible to and may be used to get the last component
557 * among others which may list the same aliases.
558 *
559 * @tparam Self Deducing this
560 * @param self Deducing this
561 *
562 * @return
563 * If the composable class also inherits from `TSharedFromThis` return a shared ref.
564 */
565 template <CSharedFromThis Self, typename... ValidAs>
566 auto With(this Self&& self, TTypes<ValidAs...>&&)
567 {
568 Forward<Self>(self).template AddAlias<ValidAs...>();
569 return StaticCastSharedRef<std::decay_t<Self>>(self.AsShared());
570 }
571
572 /**
573 * @brief
574 * Add a list of types the last added component is convertible to and may be used to get the last component
575 * among others which may list the same aliases.
576 *
577 * Usage:
578 * @code
579 * auto result = IComposable()
580 * .WithComponent<FMyComponent>().With(TAlias<
581 * FMyComponentBase,
582 * IMyComponentInterface
583 * >)
584 * ;
585 * @endcode
586 *
587 * This overload is available for composable classes which are not explicitly meant to be used with shared pointers.
588 *
589 * @warning
590 * Calling this function before adding a component may result in a runtime crash!
591 *
592 * @tparam ValidAs
593 * The list of other types the last added component is convertible to and may be used to get the last component
594 * among others which may list the same aliases.
595 *
596 * @tparam Self Deducing this
597 * @param self Deducing this
598 *
599 * @return
600 * Perfect-forwarded self.
601 */
602 template <typename Self, typename... ValidAs>
603 requires (!CSharedFromThis<Self>)
604 decltype(auto) With(this Self&& self, TTypes<ValidAs...>&&)
605 {
606 Forward<Self>(self).template AddAlias<ValidAs...>();
607 return Forward<Self>(self);
608 }
609
610 /**
611 * @brief
612 * Get all components added matching~ or aliased by the given type.
613 *
614 * @tparam T Desired component type.
615 *
616 * @return
617 * A range-view containing all the matched components. Components are provided as pointers to ensure they're
618 * not copied even under intricate object plumbing situations, but invalid pointers are never returned.
619 * (as long as the composable class is alive of course)
620 */
621 template <typename T>
622 ranges::any_view<T*> GetComponents() const
623 {
624 namespace rv = ranges::views;
625 return GetComponentsPrivate(TTypeHash<T>)
626 | rv::transform([](FAny* component) { return component->TryGet<T>(); })
627 | FilterValid();
628 }
629
630 /**
631 * @brief
632 * Get the first component matching~ or aliased by the given type.
633 *
634 * The order of components are non-deterministic so this method only make sense when it is trivial that only
635 * one component will be available for that particular type.
636 *
637 * @tparam T Desired component type.
638 *
639 * @return
640 * A pointer to the component if one at least exists, nullptr otherwise.
641 */
642 template <typename T>
643 const T* TryGetComponent() const
644 {
645 return GetComponents<T>() | FirstOrDefault();
646 }
647
648 /**
649 * @brief
650 * Get the first component matching~ or aliased by the given type.
651 *
652 * The order of components are non-deterministic so this method only make sense when it is trivial that only
653 * one component will be available for that particular type.
654 *
655 * @tparam T Desired component type.
656 *
657 * @return
658 * A pointer to the component if one at least exists, nullptr otherwise.
659 */
660 template <typename T>
662 {
663 return GetComponents<T>() | FirstOrDefault();
664 }
665
666 /**
667 * @brief
668 * Get the first component matching~ or aliased by the given type.
669 *
670 * The order of components are non-deterministic so this method only make sense when it is trivial that only
671 * one component will be available for that particular type.
672 *
673 * @warning
674 * If there may be the slightest doubt that the given component may not exist on this composable class, use
675 * `TryGetComponent` instead as this function can crash at runtime.
676 *
677 * @tparam T Desired component type.
678 *
679 * @return
680 * A reference to the desired component. It is a runtime crash if the component doesn't exist.
681 */
682 template <typename T>
683 T const& GetComponent() const
684 {
685 const T* result = TryGetComponent<T>();
686 ASSERT_CRASH(result, ->WithMessageF(TEXT_"Component {0} was unavailable.", TTypeName<T>));
687 return *result;
688 }
689
690 /**
691 * @brief
692 * Get the first component matching~ or aliased by the given type.
693 *
694 * The order of components are non-deterministic so this method only make sense when it is trivial that only
695 * one component will be available for that particular type.
696 *
697 * @warning
698 * If there may be the slightest doubt that the given component may not exist on this composable class, use
699 * `TryGetComponent` instead as this function can crash at runtime.
700 *
701 * @tparam T Desired component type.
702 *
703 * @return
704 * A reference to the desired component. It is a runtime crash if the component doesn't exist.
705 */
706 template <typename T>
708 {
709 T* result = TryGetComponent<T>();
710 ASSERT_CRASH(result, ->WithMessageF(TEXT_"Component {0} was unavailable.", TTypeName<T>));
711 return *result;
712 }
713 };
714}
#define ASSERT_CRASH(condition,...)
Use this instead of check macro if the checked expression shouldn't be ignored in shipping builds....
Bring modern declarative range operations like views and actions to the Unreal C++ arsenal....
Use leading TEXT_ without parenthesis for Unreal compatible text literals.
#define TEXT_
A convenience alternative to Unreal's own TEXT macro but this one doesn't require parenthesis around ...
Definition TextMacros.h:51
A base class which can bring type based class-composition to a derived class.
decltype(auto) WithComponent(this Self &&self, TAnyTypeFacilities< MainType > const &facilities={})
Add a default constructed component to this composable class with a fluent API.
T & GetComponent()
Get the first component matching~ or aliased by the given type.
auto WithAlias(this Self &&self)
Add a type, the last added component is convertible to and may be used to get the last component amon...
decltype(auto) WithAlias(this Self &&self)
Add a type, the last added component is convertible to and may be used to get the last component amon...
decltype(auto) With(this Self &&self, TTypes< ValidAs... > &&)
Add a list of types the last added component is convertible to and may be used to get the last compon...
const T * TryGetComponent() const
Get the first component matching~ or aliased by the given type.
void AddAlias()
Add a list of types the last added component is convertible to and may be used to get the last compon...
decltype(auto) WithComponent(this Self &&self, MainType *newComponent, TAnyTypeFacilities< MainType > const &facilities={})
Add a component to this composable class with a fluent API.
void AddComponent(this Self &&self, TAnyTypeFacilities< MainType > const &facilities={})
Add a default constructed component to this composable class.
ranges::any_view< T * > GetComponents() const
Get all components added matching~ or aliased by the given type.
auto With(this Self &&self, TTypes< ValidAs... > &&)
Add a list of types the last added component is convertible to and may be used to get the last compon...
T const & GetComponent() const
Get the first component matching~ or aliased by the given type.
auto WithComponent(this Self &&self, MainType *newComponent, TAnyTypeFacilities< MainType > const &facilities={})
Add a component to this composable class with a fluent API.
auto WithComponent(this Self &&self, TAnyTypeFacilities< MainType > const &facilities={})
Add a default constructed component to this composable class with a fluent API.
T * TryGetComponent()
Get the first component matching~ or aliased by the given type.
void AddComponent(this Self &&self, MainType *newComponent, TAnyTypeFacilities< MainType > const &facilities={})
Add a component to this composable class.
void ForEachExplicitBase(Function &&function)
Definition Any.h:81
Namespace containing utilities and base classes for type composition.
Definition Composition.h:24
decltype(auto) FirstOrDefault(Input &&range)
Get's the first element of a range or return the default value for the element type....
Definition Views.h:86
FORCEINLINE auto FilterValid()
Definition Views.h:179
A simplistic but type-safe and RAII compliant storage for anything. Enclosed data is owned by this ty...
Definition Any.h:149
const T * TryGet() const
Definition Any.h:192
Give the opportunity to customize object lifespan operations for FAny by either specializing this tem...
Definition Any.h:103
This template is used in IComponent|FAny::With(TAlias<...>) so it can have deduced this type and expl...
Definition Any.h:31
Inherit from this empty interface to signal that the inheriting class knows that it's a component and...
Definition Composition.h:40
Inherit from this empty interface to signal that the inheriting class knows that it's a component and...
Definition Composition.h:54