14#include "CoreMinimal.h"
100 template <
typename T,
typename Composition>
102 &&
requires(std::decay_t<T>& t, Composition&& parent) { t.OnCreatedAt(parent); }
105 template <
typename T,
typename Composition>
107 &&
requires(std::decay_t<T>& t, Composition&& parent, T
const& from) { t.OnCopiedAt(parent, from); }
110 template <
typename T,
typename Composition>
112 &&
requires(std::decay_t<T>& t, Composition&& parent) { t.OnMovedAt(parent); }
115 template <
typename T,
typename Composition>
118 template <
typename T,
typename Composition>
273 struct FComponentLogistics
275 TFunction<void(
IComposable* target,
FAny const& targetComponent)> Copy;
280 mutable TMap<FTypeHash, FAny> Components;
281 mutable TMap<FTypeHash, FComponentLogistics> ComponentLogistics;
282 mutable TMap<FTypeHash, TArray<FTypeHash>> ComponentAliases;
284 bool HasExactComponent(
FTypeHash typeHash)
const;
285 bool HasComponentAliasUnchecked(
FTypeHash typeHash)
const;
286 bool HasComponentAlias(
FTypeHash typeHash)
const;
289 void NotifyCopyComponents(
IComposable const& other);
291 void ResetComponents();
293 template <
typename Val
idAs>
294 void AddComponentAlias(
FTypeHash mainType)
296 Components[mainType].WithAlias<ValidAs>();
297 AddComponentAlias(mainType, TTypeHash<ValidAs>);
299 if constexpr (CHasBases<ValidAs>)
301 ForEachExplicitBase<ValidAs>([&,
this] <
typename Base> ()
303 AddComponentAlias(mainType, TTypeHash<Base>);
308 ranges::any_view<FAny*> GetExactComponent(
FTypeHash typeHash)
const;
309 ranges::any_view<FAny*> GetAliasedComponents(
FTypeHash typeHash)
const;
353 template <
typename MainType,
typename Self>
358 ASSERT_CRASH(!self.HasExactComponent(TTypeHash<MainType>),
360 TEXT_"{0} cannot be added because another component already exists under that type.",
364 "Try wrapping your component in an empty derived type, and register it with its base type {0} as its"
365 " alias. Later on both the current and the already existing component can be accessed via"
366 " `GetComponents<{0}>()` which returns a range of all matching components.",
371 FAny& boxedComponent = self.Components.Add(TTypeHash<MainType>,
FAny(newComponent, facilities));
372 MainType* unboxedComponent = boxedComponent.
TryGet<MainType>();
374 self.LastAddedComponentHash = TTypeHash<MainType>;
375 if constexpr (CHasBases<MainType>)
377 ForEachExplicitBase<MainType>([&] <
typename Base> ()
380 self.AddComponentAlias(TTypeHash<MainType>, TTypeHash<Base>);
384 if (self.OnComponentAdded) self.OnComponentAdded(boxedComponent);
385 if constexpr (CCompatibleExplicitComponent<MainType, Self>)
387 unboxedComponent->OnCreatedAt(self);
388 if constexpr (CCopyAwareComponent<MainType, Self> || CMoveAwareComponent<MainType, Self>)
390 self.ComponentLogistics.Add(TTypeHash<MainType>, {
391 .Copy = [unboxedComponent](IComposable* target,
FAny const& targetBoxedComponent)
394 if constexpr (CCopyAwareComponent<MainType, Self>)
396 auto targetComponent = AsMutablePtr(targetBoxedComponent.TryGet<MainType>());
399 TEXT_"{0} component cannot be copied as its destination wrapper was incompatible.",
403 targetComponent->OnCopiedAt(*
static_cast<Self*
>(target), *unboxedComponent);
406 .Move = [unboxedComponent](IComposable* target)
409 if constexpr (CMoveAwareComponent<MainType, Self>)
410 unboxedComponent->OnMovedAt(*
static_cast<Self*
>(target));
428 template <CDefaultInitializable MainType,
typename Self>
429 requires CCompatibleComponent<MainType, Self>
432 FWD(self).template AddComponent<MainType, Self>(
new MainType(), facilities);
447 template <
typename... ValidAs>
450 ASSERT_CRASH(LastAddedComponentHash != 0 && Components.Contains(LastAddedComponentHash),
451 ->WithMessage(
TEXT_"Component aliases were listed, but no components were added before.")
452 ->WithDetails(
TEXT_"Make sure `AddAlias` or `WithAlias` is called after `AddComponent` / `With`.")
454 (AddComponentAlias<ValidAs>(LastAddedComponentHash), ...);
478 template <
typename MainType, CSharedFromThis Self>
482 FWD(self).template AddComponent<MainType, Self>(newComponent, facilities);
504 template <
typename MainType, CSharedFromThis Self>
505 requires CCompatibleComponent<MainType, Self>
506 auto WithAnsi(
this Self&& self, MainType* newComponent)
508 FWD(self).template AddComponent<MainType, Self>(newComponent, AnsiAnyFacilities<MainType>);
528 template <CDefaultInitializable MainType, CSharedFromThis Self>
532 FWD(self).template AddComponent<MainType, Self>(facilities);
553 template <CDefaultInitializable MainType, CSharedFromThis Self>
554 requires CCompatibleComponent<MainType, Self>
582 template <
typename MainType,
typename Self>
586 FWD(self).template AddComponent<MainType, Self>(newComponent, facilities);
608 template <
typename MainType,
typename Self>
609 requires (CCompatibleComponent<MainType, Self> && !CSharedFromThis<Self>)
610 decltype(
auto)
WithAnsi(
this Self&& self, MainType* newComponent)
612 FWD(self).template AddComponent<MainType, Self>(newComponent, AnsiAnyFacilities<MainType>);
632 template <CDefaultInitializable MainType,
typename Self>
636 FWD(self).template AddComponent<MainType, Self>(facilities);
654 template <CDefaultInitializable MainType,
typename Self>
655 requires (CCompatibleComponent<MainType, Self> && !CSharedFromThis<Self>)
696 template <
typename Val
idAs, CSharedFromThis Self>
699 FWD(self).template AddAlias<ValidAs>();
737 template <
typename Val
idAs,
typename Self>
738 requires (!CSharedFromThis<Self>)
741 FWD(self).template AddAlias<ValidAs>();
775 template <CSharedFromThis Self,
typename... ValidAs>
778 FWD(self).template AddAlias<ValidAs...>();
812 template <
typename Self,
typename... ValidAs>
813 requires (!CSharedFromThis<Self>)
816 FWD(self).template AddAlias<ValidAs...>();
844 CSharedFromThis Self,
845 CFunctionLike Function
847 requires (TFunction_ArgCount<Function> == 1)
848 auto With(
this Self&& self, Function&& function)
879 CFunctionLike Function
881 requires (!CSharedFromThis<Self> && TFunction_ArgCount<Function> == 1)
882 decltype(
auto)
With(
this Self&& self, Function&& function)
899 template <
typename T>
902 namespace rv = ranges::views;
903 return GetComponentsDynamic(TTypeHash<T>)
904 | rv::transform([](
FAny* component) {
return component->
TryGet<T>(); })
920 template <
typename T>
923 return GetComponents<T>() |
First(
nullptr);
938 template <
typename T>
941 return GetComponents<T>() |
First(
nullptr);
960 template <
typename T>
963 const T* result = TryGet<T>();
964 ASSERT_CRASH(result, ->WithMessageF(
TEXT_"Component {0} was unavailable.", TTypeName<T>));
984 template <
typename T>
987 T* result = TryGet<T>();
988 ASSERT_CRASH(result, ->WithMessageF(
TEXT_"Component {0} was unavailable.", TTypeName<T>));
#define ASSERT_CRASH(condition,...)
Use this instead of check macro if the checked expression shouldn't be ignored in shipping builds....
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
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 ...
A base class which can bring type based class-composition to a derived class.
decltype(auto) With(this Self &&self, TAnyTypeFacilities< MainType > const &facilities={})
Add a default constructed component to this composable class with a fluent API.
auto With(this Self &&self, MainType *newComponent, TAnyTypeFacilities< MainType > const &facilities={})
Add a component to this composable class with a fluent API.
decltype(auto) WithAnsi(this Self &&self, MainType *newComponent)
Add a component to this composable class with a fluent API, enforcing standard memory allocators.
auto WithAnsi(this Self &&self)
Add a default constructed component to this composable class with a fluent API, enforcing standard me...
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...
T & Get()
Get the first component matching~ or aliased by the given type.
decltype(auto) With(this Self &&self, MainType *newComponent, TAnyTypeFacilities< MainType > const &facilities={})
Add a component to this composable class with a fluent API.
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...
T const & Get() 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...
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.
IComposable(IComposable &&other) noexcept
IComposable(const IComposable &other)
decltype(auto) WithAnsi(this Self &&self)
Add a default constructed component to this composable class with a fluent API, enforcing standard me...
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 * TryGet()
Get the first component matching~ or aliased by the given type.
ranges::any_view< FAny * > GetComponentsDynamic(FTypeHash typeHash) const
Get components determined at runtime.
auto With(this Self &&self, TAnyTypeFacilities< MainType > const &facilities={})
Add a default constructed component to this composable class with a fluent API.
const T * TryGet() const
Get the first component matching~ or aliased by the given type.
auto WithAnsi(this Self &&self, MainType *newComponent)
Add a component to this composable class with a fluent API, enforcing standard memory allocators.
TFunction< void(FAny &)> OnComponentAdded
Override this function in your composable class to do custom logic when a component is added....
decltype(auto) With(this Self &&self, Function &&function)
Modify a component inline, with a lambda function. The component type is inferred from the function's...
void AddComponent(this Self &&self, MainType *newComponent, TAnyTypeFacilities< MainType > const &facilities={})
Add a component to this composable class.
auto With(this Self &&self, Function &&function)
Modify a component inline, with a lambda function. The component type is inferred from the function's...
T * New(Args &&... args)
Force using the ANSI memory allocation behavior, instead of the Unreal default.
Namespace containing utilities and base classes for type composition.
typename TFunctionTraits< std::decay_t< T > >::template ArgDecay< I > TFunction_ArgDecay
Shorthand for getting a decayed type of a function argument at given position I.
decltype(auto) First(Input &&range, Value &&def)
Get's the first element of a range or return a provided default value. Same as *r....
FORCEINLINE auto FilterValid()
auto SharedSelf(const T *self) -> TSharedRef< T const, Mode >
Same as SharedThis(this) in TSharedFromThis.
A simplistic but type-safe and RAII compliant storage for anything. Enclosed data is owned by this ty...
Give the opportunity to customize object lifespan operations for FAny by either specializing this tem...
Inherit from this empty interface to signal that the inheriting class knows that it's a component and...
Inherit from this empty interface to signal that the inheriting class knows that it's a component and...
This template is used to store pack of types in other templates, or to allow parameter pack inference...