代码之家  ›  专栏  ›  技术社区  ›  skypjack

类型的替代ID生成器

  •  15
  • skypjack  · 技术社区  · 6 年前

    a project of mine ,我有一个 ID generator 对于与此类似的类型:

    class Family {
        static std::size_t identifier;
    
        template<typename...>
        static std::size_t family() {
            static const std::size_t value = identifier++;
            return value;
        }
    
    public:
        template<typename... Type>
        inline static std::size_t type() {
            return family<std::decay_t<Type>...>();
        }
    };
    
    std::size_t Family::identifier{};
    

    用途:

    const auto id = Family::type<FooBar>();
    

    就我的目的而言,它工作得很好,但是它有一些局限性。最让人恼火的一个(问题的目的)是,如果一个链接到共享库的可执行文件试图创建标识符,那么它在使用时会失败。结果通常是,第n个标识符跨边界分配给不同的类型,因为每个共享库都保持各自的独立 Family::identifier .

    一些 指出一个更可靠的解决方案是值得赞赏的,但没有提出一个不破坏性能的解决方案(几乎所有解决方案都引入了容器、查找功能和内存分配)。

    是否有其他方法可以在不损失当前设计性能的情况下,绕过上述限制?

    我搜索了一下,找到了一些有趣的答案。其中许多是几岁的。我想探索最新版本标准的解决方案,只要现有类的接口保持不变。
    This one 是最有趣的。它使用静态成员的地址来实现相同的目的,但是它不符合顺序生成标识符的思想。

    注释 :不幸的是,使用RTTI不是一个选项。

    注释 :id必须按顺序生成,并从0开始,如上述解决方案中所示。

    3 回复  |  直到 6 年前
        1
  •  4
  •   John Zwinck    6 年前

    出现问题的原因是您的头文件中有这一行:

    std::size_t Family::identifier{};
    

    因此,它最终出现在每个翻译单元中。相反,您需要将此存储移到一个仅编译一次的.cpp源文件中,或者移到自己的共享库中。那么只有一个实例 identifier 在程序中,它将按您的意愿工作。

    你也可以搬家 标识符 从一个班级 static 变量到全局 extern 头文件中有一个(如上所述,在单个.cpp文件中定义它)。

    如果你有C++ 17或更高版本,你也可以尝试:

    inline std::size_t Family::identifier{};
    

    虽然语言不能保证(甚至提到)当您跨共享库边界使用这个新特性时会发生什么,但它确实在我的机器上工作。

        2
  •  1
  •   Maxim Egorushkin    6 年前

    如果不要求ID是连续整数,则可以使用模板静态成员的地址作为ID。此方法的好处是不需要任何运行时初始化(使用 static initialization ):

    // in a header
    class Family {
        template<class...> struct Id { static char const id; };
    
        template<typename... T>
        static std::size_t family() {
            return reinterpret_cast<std::size_t>(&Id<T...>::id);
        }
    
    public:
        template<typename... Type>
        static std::size_t type() {
            return family<std::decay_t<Type>...>();
        }
    };
    
    // in a header
    template<class... T>
    char const Family::Id<T...>::id = {};
    
    // elsewhere    
    int main() {
        auto int_id = Family::type<int>();
        auto int_int_id = Family::type<int, int>();
    }
    

    你也可以做到 id 编译时常量,并将其用作模板参数:

    // in a header
    struct Family {
        template<class...> struct Id { static char const id; };
    };
    
    // in a header
    template<class... T>
    char const Family::Id<T...>::id = {};
    
    // elsewhere    
    template<char const*>
    struct X {};
    
    int main() {
        X<&Family::Id<int>::id> x;
    }
    
        3
  •  1
  •   Ivan Sanz Carasa    6 年前

    如果您不关心顺序ID,请使用函数的地址作为标识符。

    template<typename... T>
    uintptr_t getID() {
        return reinterpret_cast<uintptr_t>(&getID<T...>);
    }
    

    然后

    auto intID = getID<int>();
    auto floatID = getID<float>();
    ...