【问题标题】:Writing a get component method in c++ with templates使用模板在 C++ 中编写 get 组件方法
【发布时间】:2019-05-08 03:33:18
【问题描述】:

我正在尝试编写 Unity 风格的 get 组件方法。到目前为止,这是我的代码。它会编译,但会返回它找到的第一个组件,而不是正确的组件。我想我使用 static_cast 错误。有什么更好的方法来做到这一点?注意我不想硬编码组件类型,我希望能够编译这个引擎并使用从 Component 继承的任何东西来使用这个系统。另请注意,每个组件都需要返回自身,而不是组件 *,因为这会隐藏子功能。

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;
};

【问题讨论】:

  • 您可能想要dynamic_cast,但假设Component 是多态的。你能提供一个minimal reproducible example 来确定吗?
  • 动态转换需要包含该类我很确定,这在这种情况下不起作用,因为我希望能够使用任何组件类型(再次,我认为?)。无论如何,我现在将包含 MCV 示例:)
  • 如果你想接收这样一个,你需要转换为指针; static_cast 不返回空指针,除非转换的指针已经是它本身...
  • 既然我们谈论的是一个函数模板,那么在模板实例化时(即调用函数时)定义类就足够了)。我很确定使用它的代码会涵盖这些内容。
  • Dynamic Cast 修复了它。谢谢!

标签: c++ templates components


【解决方案1】:

关于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 的指针(在某些涉及多重继承的情况下可能存在偏移),但对后者的数据的解释完全不同(除非 D1D2 布局兼容)并且您可能会结束下地狱!

首先,我个人更喜欢修改后的签名:

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

它允许像getComponent&lt;SomeType&gt;()而不是getComponent&lt;SomeType*&gt;()一样调用你的函数,此外它还允许在函数体内使用指针,这更清楚,请参阅下面我适当调整的代码。

那么你真正需要的是dynamic_cast(根据我的个人喜好稍微调整你的代码......):

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

编辑:赶上Nshant Singh的评论:

dynamic_cast其实挺贵的。

另一种方法是unordered_map,替换您的向量(如何设置的示例可以在type_index documentation 找到;当然,您可以放置​​对象而不是字符串...)。那么您的查找可能如下所示:

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!

【讨论】:

  • 谢谢伙计。没有选择,因为 Angew 在评论中将其放在首位,但您的解释无疑提高了我的理解。
  • 关于指针的注意事项:没有什么能阻止 OP 保留星号并将函数调用为 getComponent&lt;ConcreteComponent*&gt;
  • @Aconcagua @WillHain 我认为基于type_indexunordered_map 会比基于dynamic_cast 的解决方案快得多,因为基于dynamic_cast 的解决方案线性检查分量向量。此外,dynamic_cast 解决方案可能会失败,如果我们的组件具有超过 2 个的派生层次结构,例如 Component-&gt;AnimationComponent-&gt;AdvancedAnimationComponentAdvancedAnimationComponentAnimationComponent 之前添加到向量中,那么 getComponent&lt;AnimationComponent*&gt; 将错误地给出 AdvancedAnimationComponent
  • @NishantSingh 另一方面,如果我们查找AnimationComponent,则类型索引可能是一个问题,但只有AdvancedAnimationComponent 驻留在地图中。好吧,我们可以为两个索引(AC 和 AAC)添加 AAC 对象来解决(或者如果我们想隐藏它,只为 AC 索引添加 AAC 对象)。
  • @WillHain 如果所有这些都具有同等价值,您应该只选择 最快 发布的答案,否则,您应该选择您考虑回答问题的一个 最好的。好吧,在这种情况下,我并没有要求我这样做,Angew 列出的(涵盖的)先决条件也很好,所以你的选择很好,请记住未来......
【解决方案2】:

static_cast 绝对不是你想要的:它是静态(编译时),所以它无法确定任何运行时信息。

你想要的是dynamic_cast。请注意,这有几个要求,所有这些都由您的代码满足:

  • 类必须是多态的。这已经涵盖了,因为Component 有一个虚拟析构函数。
  • 类必须在强制转换时定义(而不仅仅是声明)。这也包括在内,因为getComponent 是一个模板,并且强制转换中的类型取决于它的模板参数(实际上是一个)。因此,定义只需要在模板被实例化的地方可见(即getComponent 被调用的地方)。由于您可能正在执行转换以访问具体组件的成员,因此您必须使其定义可见,所以一切都很好。

【讨论】:

  • @WillHain 如果您希望拥有大量实体和/或组件并在常见操作中进行大量此类查找,请注意性能/可扩展性。根据需要,有更多高效的方式来构建 ECS,但很难给出一个直接简单的答案。
猜你喜欢
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
  • 2013-03-07
  • 1970-01-01
  • 1970-01-01
  • 2012-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多