【问题标题】:Is it possible to assign array values at compile-time through constexpr functions?是否可以在编译时通过 constexpr 函数分配数组值?
【发布时间】: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 中找到一个空闲槽来存储它。这样的事情在编译时可能吗?如果有人对我如何实现我想要的或改进解决方案有任何建议,我将不胜感激。这里没有包含一些宏代码,它们会生成 ComponentStorageMappingComponentTypeInfom_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是零初始化的,我可以构造我的编译时对象AB,但是在我的例子AddToArray中,我想构造一个T的对象并将它插入到一个槽中在m_array 中,然后返回该插槽。但是我不能这样做,因为函数是 const 并且 m_array 不能被修改。
  • 为了记录,我在此之前创建了另一个帖子 (gamedev.stackexchange.com/questions/176301/…),以了解我将如何处理这种情况并且没有收到很多建议,所以我只是尝试自己做,这是我遇到的问题。还有另一个框架以某种方式实现了我正在尝试做的事情,但我不太明白他们在做什么,因为它都是模板。该框架的链接在另一篇文章中。

标签: c++ constexpr compile-time


【解决方案1】:

我不认为这是你想要的。

看看你为AddToArray()写了什么

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

你想要一个constexpr 方法,也就是const,在类/结构的成员中添加一个组件。

显然你不能像下面这样使用它

constexpr const A& someArrayElement = AddElement<A>(5.0f, 5.0f, 5.0f);
constexpr const A& someArrayElement2 = AddElement<A>(10.0f, 10.0f, 10.0f);

如果没有它的类型的对象,你不能调用AddElement()

您应该从 C 类型的 constexpr 对象开始

constexpr C<A, 50u>  ca;

然后用它调用AddElement()

constexpr const A& someArrayElement = ca.AddElement<A>(5.0f, 5.0f, 5.0f);
constexpr const A& someArrayElement2 = ca.AddElement<A>(10.0f, 10.0f, 10.0f);
// ....................................^^^

但是如果caconstexpr 你不能在其中添加元素并且如果ca 不是constexpr 你不能使用AddElement() 的结果来初始化@987654337 的值@变量。

我认为解决此问题的唯一方法是使用所有元素初始化 ca 对象;像

constexpr C<A, 50u>  ca { A{5.0f, 5.0f, 5,0f}, A{10.0f, 10.0f, 10.0f} /* etc ... */ };

然后提取对组件的引用

constexpr A & someArrayElement  = ca.GetElement(0);
constexpr A & someArrayElement2 = ca.GetElement(1);

【讨论】:

    猜你喜欢
    • 2017-06-15
    • 1970-01-01
    • 2014-01-08
    • 1970-01-01
    • 1970-01-01
    • 2023-01-05
    • 1970-01-01
    • 1970-01-01
    • 2021-02-13
    相关资源
    最近更新 更多