【发布时间】:2019-10-27 01:33:56
【问题描述】:
我对模板和编译时函数相当陌生,我目前正在尝试编写一个基本的实体组件系统 (ECS),它允许我将泛型类型(组件)存储在容器中。我希望功能能够在编译时和运行时设置我的容器。在我的应用程序中有一些实例,我可以确定哪些组件将始终为实体保留,因此我希望能够设置这些组件并在编译时保留插槽。在某些情况下,需要在运行时动态删除/添加组件。
template <typename T, size_t N>
struct ComponentArray {
public:
template<typename... Args>
constexpr const T& AddComponent(const uint64_t entity, Args &&... args) const
{
//T component{ std::forward<Args>(args)... }; //i want to be able to do something like this..
//m_components[0] = component; //create the component and give it a slot, but this is const
return m_components[0];
}
std::array<T, N> m_components = {};
std::array<T, N> m_entityIds = {};
uint32_t m_used = 0;
};
template <uint64_t entityId, typename Component, typename ... Args>
constexpr const Component& AddComponentCT(Args&&... args)
{
constexpr uint64_t componentHash = GetHashForString(ComponentTypeInfo<Component>::m_name, ComponentTypeInfo<Component>::m_nameLength);
constexpr uint64_t storageHash = ComponentStorageMapping<componentHash>::m_hash;
static_assert(storageHash == componentHash);
constexpr const auto& componentStorage = ComponentStorageMapping<componentHash>::m_componentStorage;
constexpr const Component& rComponent = componentStorage.AddComponent(entityId /*std::forward<Args>(args)...*/);
return rComponent;
}
constexpr const TransformComponent transform = AddComponentCT<ENTITY_ID, TransformComponent>(5.0f, 5.0f, 5.0f);
这似乎有效,当我将鼠标悬停在transform 上时,我可以看到返回的内容,但显然是不正确的值,因为我无法使用 const 函数中的参数构造类型。
我也尝试从变量中删除一些 const,但它抱怨事情不是 const 或无法评估。
我希望使用提供的参数创建一个Component 类型,然后在m_components 中找到一个空闲槽来存储它。这样的事情在编译时可能吗?如果有人对我如何实现我想要的或改进解决方案有任何建议,我将不胜感激。这里没有包含一些宏代码,它们会生成 ComponentStorageMapping、ComponentTypeInfo 和 m_componentStorage 等类
以下是 cmets 中要求的更简化/通用的版本
struct A
{
float x, y, z;
};
struct B
{
int i, j, k;
};
template <typename T, size_t N>
struct C {
public:
template<typename... Args>
constexpr const T& AddToArray(Args &&... args) const
{
//T element{ std::forward<Args>(args)... }; //i want to be able to do something like this..
//m_array[0] = element; //create the component and give it a slot, but this is const
return m_array[0];
}
std::array<T, N> m_array = {};
};
template <typename T, typename ... Args>
constexpr const T& AddElement(Args&&... args)
{
constexpr const T& element = C<T, 50>::AddToArray(/*std::forward<Args>(args)...*/);
return element;
}
constexpr const A& someArrayElement = AddElement<A>(5.0f, 5.0f, 5.0f); //this would point to C<A,50>::m_array[0], fully constructed
constexpr const A& someArrayElement2 = AddElement<A>(10.0f, 10.0f, 10.0f); //this would point to C<A,50>::m_array[1], fully constructed
constexpr const B& someArrayElement3 = AddElement<B>(1, 1, 1); //this would point to C<B,50>::m_array[0], fully constructed
constexpr const B& someArrayElement4 = AddElement<B>(2, 2, 2); //this would point to C<B,50>::m_array[1], fully constructed
为了记录,我在此之前创建了另一个帖子(https://gamedev.stackexchange.com/questions/176301/how-can-i-create-a-templated-function-to-add-components-to-their-necessary-conta),以了解我将如何处理这种情况并且没有收到很多建议,所以我只是尝试自己做,这就是我遇到的问题.还有另一个框架以某种方式实现了我正在尝试做的事情,但我不太明白他们在做什么,因为它都是模板。该框架的链接在另一篇文章中。
【问题讨论】:
-
你能简化这个例子吗?现在,您正在展示许多不相关的细节(例如,组件、转换、哈希等)。删除这些并保留一个像
C这样的泛型类,其中包含一个数组成员。如果有任何与问题无关的内容,请忽略它。 -
@L.F.没问题,我会记住这一点以供将来参考。我只是认为一点上下文可能会有所帮助,但你是对的,在这种情况下可能没有帮助。我在我的帖子中添加了另一个 sn-p,它删除了大部分不必要的代码。
-
您还能简化您的要求吗?因为好吧,用最少的复制构造一个数组内部的任何东西,并且从一个常量表达式也是微不足道的(
std::array::operator[]返回一个引用,所以它基本上是“emplace”,只要你的 T 有一个constexpr构造函数你很高兴)。constexpr-初始化整个数组的零开销也很简单。但是在运行时找到一个空槽然后插入 something,而constexpr听起来几乎是不可能的(这应该如何工作?)。 -
@Damon 我不确定如何进一步简化它,或者我所问的是否可能,但我确信有一些解决方案可以实现类似的效果。所以
m_array是零初始化的,我可以构造我的编译时对象A和B,但是在我的例子AddToArray中,我想构造一个T的对象并将它插入到一个槽中在m_array中,然后返回该插槽。但是我不能这样做,因为函数是 const 并且m_array不能被修改。 -
为了记录,我在此之前创建了另一个帖子 (gamedev.stackexchange.com/questions/176301/…),以了解我将如何处理这种情况并且没有收到很多建议,所以我只是尝试自己做,这是我遇到的问题。还有另一个框架以某种方式实现了我正在尝试做的事情,但我不太明白他们在做什么,因为它都是模板。该框架的链接在另一篇文章中。
标签: c++ constexpr compile-time