【问题标题】:Creating a unique id by combining different types通过组合不同的类型创建唯一的 id
【发布时间】:2020-10-30 11:55:13
【问题描述】:

我想通过“组合”类型来创建一个唯一的 id。输入类型的顺序无关紧要,输入相同的类型组合应始终返回相同的 id(在相同的运行时)。我想在实体组件系统中使用它来识别组件的组合。

一个几乎可以完成工作的想法:

    class Archetype
    {
    public:
        template <typename... Types>
        static uint32_t CreateArchetype()
        {
            return GetArchetypeIndex<Types...>();
        }
 
    private:
        template <typename... Types>
        static uint32_t GetArchetypeIndex()
        {
            static uint32_t index = GetNewArchetypeIndex();
            return index;
        }

        static uint32_t GetNewArchetypeIndex()
        {
            static uint32_t lastID = 0u;
            return ++lastID;
        }
    };
int main()
{
    std::cout << Archetype::CreateArchetype<uint16_t, uint32_t, int, bool>() << std::endl;
    std::cout << Archetype::CreateArchetype<uint16_t, uint32_t, int, bool>() << std::endl;

    // Same Types but different order
    std::cout << Archetype::CreateArchetype<int, bool, uint16_t, uint32_t>() << std::endl;
    std::cout << Archetype::CreateArchetype<int, bool, uint16_t, uint32_t>() << std::endl;
}

Output: 1, 1, 2, 2Goal: 1, 1, 1, 1

这将返回一个唯一的 id,但它确实关心订单。也许这可以通过某种“可变模板排序魔法”以某种方式对CreateArchetype() 函数中的类型进行排序来解决,但我还没有设法做到这一点,有可能还是有其他选择?

【问题讨论】:

    标签: c++ templates generic-programming entity-component-system


    【解决方案1】:

    好吧,可能有一种方法可以实现“可变模板排序魔术”,但如果您不需要在编译时使用 ID,也不需要对其进行排序,您可以对每个子目录使用 std::type_info::hash_code类型执行一些非花哨的交换操作(如求和),并将此结果用作 id。

    但是,请考虑到此结果不能保证是唯一的,您可能希望使用这些类型的名称,对结果进行排序和连接,然后对其进行哈希处理。

    我编辑了您的代码,以展示使用求和的简单方法的示例。

    #include <iostream>
    #include <valarray>
    #include <typeinfo>
    
    
        class Archetype
        {
        public:
            template <typename... Types>
            static uint32_t CreateArchetype()
            {
                return GetArchetypeIndex<Types...>();
            }
    
        private:
            template <typename... Types>
            static uint32_t GetArchetypeIndex()
            {
                std::valarray<size_t> a = { typeid(Types).hash_code()... };
                return a.sum();
                //static uint32_t index = GetNewArchetypeIndex();
                //return index;
            }
    
            static uint32_t GetNewArchetypeIndex()
            {
                static uint32_t lastID = 0u;
                return ++lastID;
            }
        };
    
    int main()
    {
        std::cout << Archetype::CreateArchetype<uint16_t, uint32_t, int, bool>() << std::endl;
        std::cout << Archetype::CreateArchetype<uint16_t, uint32_t, int, bool>() << std::endl;
    
        // Same Types but different order
        std::cout << Archetype::CreateArchetype<int, bool, uint16_t, uint32_t>() << std::endl;
        std::cout << Archetype::CreateArchetype<int, bool, uint16_t, uint32_t>() << std::endl;
    }
    

    【讨论】:

    • 感谢您的回答。在编辑这个问题之前,我实际上做了类似的事情并以它为例。但我认为由于冲突风险,散列可能不是最好的解决方案。而且我认为碰撞风险可能更大,因为使用的数据类型可能有很多组合使用几乎相同的类型,但我可能错了。
    • 这就是为什么我建议使用类型名称构建一个字符串,然后对其进行哈希处理。字符串散列用于 stl 的关联容器中,我没有看到冲突,所以我猜它不应该发生在 ECS 中。
    • 如果你遇到了冲突,或者你担心你可能会有一些冲突,你也可以使用加密哈希函数,并避免使用 unsigned_map 重新计算“强”哈希(有方法处理冲突)。
    【解决方案2】:

    对我来说,最简单的解决方案是为每个密集包装的组件类型提供唯一索引,即使用位集作为唯一 ID。例如:

    // Order doesn't matter.
    std::bitset<total_num_types> ent_id = (1ull << uint16_t_idx) |
                                          (1ull << uint32_t_idx) |
                                          (1ull << int_idx) |
                                          (1ull << bool_idx);
    

    可以把它放在一个漂亮的可变参数函数模板后面。

    【讨论】:

      猜你喜欢
      • 2021-04-05
      • 1970-01-01
      • 1970-01-01
      • 2019-10-07
      • 2021-12-14
      • 2014-08-06
      • 1970-01-01
      • 1970-01-01
      • 2020-10-01
      相关资源
      最近更新 更多