Home > Enterprise >  C Template method return ref to private map value where value's type is parent of T
C Template method return ref to private map value where value's type is parent of T

Time:12-01

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.

  • Related