Написание метода get компонента в c ++ с шаблонами - PullRequest
3 голосов
/ 10 апреля 2019

Я пытаюсь написать метод компонента в стиле Unity.Это мой код до сих пор.Он компилируется, но возвращает первый найденный компонент, а не правильный.Я думаю, что я использую static_cast неправильно.Какой лучший способ сделать это?Примечание. Я не хочу жестко кодировать типы компонентов, я хочу иметь возможность компилировать этот движок и использовать все, что унаследовано от Компонента, чтобы иметь возможность использовать эту систему.Также обратите внимание, что каждый компонент должен возвращаться как сам по себе, а не как компонент *, поскольку это скрыло бы дочернюю функциональность.

compStruct.components - это вектор компонента * s.

template <typename CompType>
inline CompType getComponent()
{
    for(Component * currComp : compStruct.components)
    {
        if (static_cast<CompType>(currComp)!= nullptr)
        {
            return static_cast<CompType>(currComp);
        }
    }
    return nullptr;
}

Здесьпример обобщенного компонента

#pragma once
#include "Component.h"
#include "Animation2D.h"

class AnimationComponent : public Component
{
public:
    AnimationComponent(GameObject*x) :Component(x) {}
    ~AnimationComponent() {}

    void stepAnimation(double delta);

    //add overload for 3d animations
    int addAnimation(Animation2D);

    void setAnimation(int);

private:

};

и базового класса компонента:

#pragma once
class GameObject;

class Component
{
public:
    Component(GameObject * h) { host = h; }
    virtual ~Component() {}

    GameObject* getHost() { return host; }

protected:
    GameObject * host = nullptr;
};

Ответы [ 2 ]

4 голосов
/ 10 апреля 2019

static_cast определенно не то, что вам нужно: это статический (время компиляции), поэтому он не может определить какую-либо информацию времени выполнения.

Вместо этого вам нужно dynamic_cast.Обратите внимание, что для этого есть несколько требований, и все они выполняются вашим кодом:

  • Классы должны быть полиморфными.Это покрыто, потому что Component имеет виртуальный деструктор.
  • Классы должны быть определены (не только объявлены) в точке приведения.Это также рассматривается, потому что getComponent является шаблоном, а тип в приведении зависит от его параметров шаблона (на самом деле это один).Следовательно, определение нужно видеть только там, где создается экземпляр шаблона (т. Е. Где вызывается getComponent).Поскольку вы, по-видимому, выполняете приведение для доступа к элементам конкретного компонента, вы должны иметь его определение видимым, чтобы все было хорошо.
3 голосов
/ 10 апреля 2019

Существует некоторое фундаментальное недопонимание относительно static_cast: он просто делает приведение, и ваша ответственность за обеспечение того, чтобы приведенный указатель действительно указывал на объект целевого типа. , static_cast будет возвращать нулевой указатель только в том случае, если указатель источника уже был сам по себе, но никогда при несоответствии типов!

class B { /*...*/ };
class D1 : public B { };
class D2 : public B { };

D1 d1;
B* b = &d1;
D2* d2 = static_cast<D2*>(b);

d2 будет указателем на d1 (в некоторых случаях, включающих множественное наследование, может быть смещение), но интерпретируйте данные последнего совершенно по-разному (если D1 и D2 не совместимы с компоновкой), и вы можете закончить в аду!

Теперь, во-первых, я лично предпочитаю модифицированную подпись:

template <typename CompType>
inline CompType* getComponent();
//             ^

Позволяет вызывать вашу функцию как getComponent<SomeType>() вместо getComponent<SomeType*>(), кроме того, она позволяет использовать указатели внутри тела функции, что является более понятным, см. Мой соответствующий код, приведенный ниже.

Тогда то, что вам на самом деле нужно, это dynamic_cast (немного подгоняя ваш код под мои личные предпочтения ...):

CompType* result = nullptr; // pointer: see above!
for(Component * currComp : compStruct.components)
{
    result = dynamic_cast<CompType*>(currComp);
    if(result)
        break;
}
return result;

Редактировать: Догоняя Ншант Сингх Комментарий:

dynamic_cast на самом деле довольно дорого.

Альтернативой может быть unordered_map, заменяющий ваш вектор (пример настройки можно найти в type_index документация ; конечно, вы бы поместили ваши объекты вместо строк .. .). Тогда ваш поиск может выглядеть так:

auto i = map.find(std::type_index(typeid(CompType));
return i == map.end() ? nullptr : static_cast<CompType*>(i->second);
// now, you CAN use static cast, as the map lookup provided you the
// necessary guarantee that the type of the pointee is correct!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...