构建模板化类型的编译时列表?

时间:2016-01-18 22:03:40

标签: c++ templates c++11 metaprogramming c++14

抱歉这个问题含糊不清。我遇到了一个似乎应该可以解决的问题,但我在调查中有点死路一条。简而言之,我希望有一个编译时列表,其中包含从A类继承的所有类型。话虽如此,我会直接解决我面临的问题。

假设我有两种类型:实体和组件。

class Entity {
public:
    entity_id id;
    std::vector<component_id> components;
};

template<typename UnderlyingComponentType>
class Component {
public:
     static std::vector<UnderlyingComponentType> components;
     component_id id;
     entity_id containingEntity;
}

在一个简单的例子中,每个实体包含多个不同类型的组件(例如TextComponent,SoundComponent,WeightComponent)。但是,不是将这些组件直接包含在字段中,而只是将它们的ID存储在实体中。这允许对相同类型的组件进行快速迭代,因为它们都在一个连续的向量中(例如,可以一次遍历所有的DescriptionComponents)。同样,可以为任何给定的实体检索给定类型的任何组件:

DescriptionComponent& decription = myEntity.getComponent<DescriptionComponent>()
// Can go to the DescriptionComponent's static vector, and look up the
// appropriate component given our entity's ID (matching it with the component's
// containingEntity ID.)

可以提供类似的功能来删除单个组件或添加。但是,当我想删除给定实体的所有组件时,我遇到了问题。因为组件存储在实体之外,所以没有一种简单的方法可以说“遍历所包含的组件并将其全部删除”。相反,我们必须为我们包含的每个组件类型转到底层静态向量,并从那里删除。这是我碰到砖墙的地方。这里似乎有两种可能的解决方案:

1。)在每个实体中存储一组类型,这些类型映射到我们包含的所有组件类型的组件向量(例如entity.getComponentTypes == X<DescriptionComponent, SoundComponent>;这里的缺点是每个实体的组件可以不断变化(我可以在上面的例子中,在运行时添加一个WeightComponent。因此,我不相信有一种方法可以根据运行时类型信息链接到编译时类型(如type_index es或者等)

2。)生成实现Component的每种类型的编译时列表,然后当Entity要求删除其所有组件时,遍历它们 all 。例如,假设我们有一个实体e,其标识为123,并包含DescriptionComponentSoundComponent。我们仍然会有一个包含[DescriptionComponent, SoundComponent, WeightComponent]的类型的编译时列表,我们会告诉每个Component的静态向量删除123引用的任何组件。由于只有DescriptionComponentSoundComponent向量具有这些引用,因此只有那两个会删除组件,并且e的所有组件都将被删除。

上面的选项2似乎是更可行的选项:创建类型的编译时列表。手动编写代码很容易:std::tuple<DescriptionComponent, SoundComponent, WeightComponent>可以轻松存储并允许我根据需要迭代这些类型。但是,我理想的是一种生成此列表的方法以编程方式,因此,如果我稍后进入并添加NameComponent,我将不必更新任何其他代码:当我从模板Component类继承时,它应该自动管理。例如,

class NameComponent : public Component<NameComponent> {
    //implementation details
}

//Now, my all-types list should contain [DescriptionComponent, SoundComponent,
// WeightComponent, NameComponent], and I didn't have to update any config.

我完全不确定上述内容是否可行,但任何帮助或资源建议都是可行的。我在this StackOverflow question看过类似的问题,但它依赖于宏(据我所知,它不会以相同的方式与模板交互)。同样地,如果看起来这是一个A / B问题,并且像type_index地图那样的简单解决方案实际上工作,那么这种批评也会受到欢迎!我确信我忽略了一些细节或措辞不好,所以如果/何时需要更多信息,请随时告诉我。

2 个答案:

答案 0 :(得分:1)

您可以使用类型擦除并存储知道要处理哪种Component类型的元素,而不是将component_id存储在实体的向量中。

#include <memory>

struct component_id{};

struct component_type_ref
{
  template <typename Comp>
  component_type_ref()
      : _id(Comp::id), _impl(std::make_shared<_impl_t<Comp>>())
  {
  }

  component_id id() const
  {
    return _id;
  }

  void clear()
  {
    _impl->clear();
  }

private:
  struct _impl_base
  {
    virtual void clear();
  };

  template <typename Comp>
  struct _impl_t : public _impl_base
  {
    void clear()
    {
      Comp::components.clear();
    }
  };

  component_id _id;
  std::shared_ptr<_impl_base> _impl;
};

另见https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil

这样,例如,您可以在实体的向量中查找组件ID,并在其上调用clear。因此,如果您想要在组件类型上运行已知的操作列表(并且这些操作不需要是模板),那么可能是一个解决方案。

如果不是这样,那么我恐怕你需要使用类型向量。这里不需要使用元组,简单

template<typename... T> struct my_type_vector{};

很可能

就足够了。但正如你自己写的那样,这在运行时并不那么可行。

答案 1 :(得分:0)

您可以使用以下内容制作与std::type_index类似的内容:

template<typename T>
void type_id() {}

using type_id_t = void(*)();

现在在你的地图中,使用:

std::map<type_id_t, /* whatever */> myMap;

并像这样检索:

auto myElement = myMap[type_id</* somthing else */>];

这将在没有RTTI的情况下完成工作