I have a problem blowing my mind actually, and a challenge for someone, could you help me with that ? :
class UItemEntity : public UObject
{
GENERATE_BODY()
public:
template<typename T=FItemComponent>
T& GetComponent()
{
auto Result = Components[TYPE_ID(T)];
T Comp = reinterpret_cast<T>(Result);
return Comp;
}
private:
/** Map from Component's type ID to one of their instance associated to this entity. */
TMap<const char*, FItemComponent> Components;
}
(It's Unreal c code but still c :P)
Example of usage :
struct FTestComponent : public FItemComponent { }
UItemEntity* Item;
FTestComponent& Comp = Item->GetComponent<FTestComponent>();
I cannot figure out how to cast the value retrieved from the map... I tried static, dynamic and reinterpret cast but none succeeded.
Here is the kind of error I have :
ItemEntity.h: [C2440] 'reinterpret_cast': cannot convert from 'ValueType' to 'T'
It might be an architecture problem, but I don't know how to work around :/ And I really need a GetComponent on this ItemEntity.
Thanks !
EDIT :
With the help of @Frank, I finally succeed in, there was some Unreal stuff to take into account.
Here it is :
class UItemEntity : public UObject
{
GENERATE_BODY()
public:
template<typename T=FItemComponent>
T& GetComponent()
{
const bool IsItemComponentType = std::is_base_of_v<FItemComponent, T>;
check(IsItemComponentType);
FItemComponent* ItemComp = *Components.Find(TYPE_ID(T));
return Cast<T>(ItemComp);
}
private:
/** Map from Component's type ID to one of their instance associated to this entity. */
TMap<const char*, FItemComponent*> Components;
}
CodePudding user response:
There may be some Unreal-specific shenanigans at play that I'm not aware of, but in general-purpose C code, it would look like this:
class UItemEntity : public UObject
{
GENERATE_BODY()
public:
template<typename T>
T& GetComponent()
{
static_assert(std::is_base_of_v<FItemComponent, T>);
return *static_cast<T*>(Components.at(TYPE_ID(T)).get());
}
private:
/** Map from Component's type ID to one of their instance associated to this entity. */
TMap<const char*, std::unique_ptr<FItemComponent>> Components;
}
Explanation:
- Elements are stored as pointers since different types can be stored in it.
at()
is used because there is nothing the function can return if the entry is missing.static_cast<>
lets the compiler known that the casting from base to derived is intentional.- The pointer is dereferenced into a reference with
*
. - I replaced the default template parameter with a
static_assert()
, according to the discussion in the comments.
You may want to use std::shared_ptr
, std::weak_ptr
, std::reference_wrapper
or even a raw pointer instead of std::unique_ptr
depending on the circumstances. However, it's hard to tell which is the correct one without some more context, so I used the baseline unique_ptr
for this example.