【问题标题】:Creating serializeable unique compile-time identifiers for arbitrary UDT's为任意 UDT 创建可序列化的唯一编译时标识符
【发布时间】:2011-01-09 18:50:22
【问题描述】:

我想要一种为任何 C++ 用户定义类型创建唯一编译时标识符的通用方法。
例如:

unique_id<my_type>::value == 0 // true
unique_id<other_type>::value == 1 // true  

我已经设法使用预处理器元编程来实现类似的东西,但问题是,序列化并不一致。例如,如果类模板 unique_id 首先用 other_type 实例化,那么我的程序以前版本中的任何序列化都将失效。

我已经搜索过这个问题的解决方案,如果唯一值是编译时常量,我发现有几种方法可以通过不一致的序列化来实现这一点。如果使用 RTTI 或类似方法,如 boost::sp_typeinfo,则唯一值显然不是编译时常量,并且存在额外开销。这个问题的一个临时解决方案是,以正确的顺序在单独的标头中实例化所有 unique_id,但这会导致额外的维护和样板代码,这与使用 enum unique_id{my_type, other_type}; 没有什么不同。

这个问题的一个很好的解决方案是使用用户定义的文字,不幸的是,据我所知,目前没有编译器支持它们。语法是 'my_type'_id; 'other_type'_id; 和 udl。

我希望有人知道一个技巧,它允许使用当前标准 (C++03/C++0x) 在 C++ 中实现可序列化的唯一标识符,如果它与最新的稳定 MSVC 和GNU-G++ 编译器,虽然我希望有解决方案,但它不是可移植的。

我想澄清一下,使用mpl::set 或类似mpl::vector 和过滤的构造并不能解决这个问题,因为元集/向量的范围是有限的,实际上导致的问题不仅仅是预处理器元编程。

【问题讨论】:

    标签: c++ c-preprocessor metaprogramming


    【解决方案1】:

    不久前,我在我的一个项目中添加了一个构建步骤,这使我可以在 C++ 源文件中编写 @script_name(args) 并自动将其替换为相关脚本的输出,例如 ./script_name.pl args 或 @ 987654323@。

    您可能不愿意将语言污染到非标准 C++ 中,但您所要做的就是编写 @sha1(my_type) 以获得类名的唯一整数散列,而不管构建顺序如何,并且不需要显式实例化。

    这只是许多可能的非标准解决方案之一,我认为这是一个相当干净的解决方案。目前没有很好的方法在你的类上强加一个任意的、一致的顺序而不只是明确地指定它,所以我建议你简单地放弃并走明确的实例化路线;集中信息并没有什么问题,但正如你所说,它与枚举并没有什么不同,这正是我在这种情况下实际使用的。

    【讨论】:

    • 这似乎是一个合理的解决方案。在主要编译器中实现 udl 之前,我将求助于此。
    【解决方案2】:

    数据的持久性是一个非常有趣的问题。

    我的第一个问题是:你真的想要 序列化 吗?如果您愿意研究替代方案,请跳至下一部分。

    如果您还在那里,我认为您还没有充分利用 typeid 解决方案。

    // static detection
    template <typename T>
    size_t unique_id()
    {
      static size_t const id = some_hash(typeid(T)); // or boost::sp_typeinfo
      return id;
    }
    
    // dynamic detection
    template <typename T>
    size_t unique_id(T const& t)
    {
      return some_hash(typeid(t)); // no memoization possible
    }
    

    注意:我使用本地静态来避免初始化顺序问题,以防在输入 main 之前需要此值

    它与您的unique_id&lt;some_type&gt;::value 非常相似,即使它是在运行时计算的,它也只计算一次,然后将结果(用于静态检测)存储起来以供将来调用。

    另请注意,它是完全通用的:无需为每种类型显式编写函数。


    这可能看起来很傻,但是序列化的问题是你在类型和它的表示之间有一个一对一的映射:

    • 您需要对表示进行版本化,以便能够解码“旧”版本
    • 处理前向兼容性非常困难
    • 处理循环引用非常困难(某些框架会处理它)
    • 然后是信息从一个转移到另一个的问题 --> 反序列化旧版本变得混乱和令人沮丧

    对于持久保存,我通常建议使用专用 BOM。将保存的数据视为给未来自己的信息。而且我通常会加倍努力并提出很棒的 Google Proto Buffer 库:

    • 内嵌向后和向前兼容性
    • 多种格式输出 -> 人类可读(用于调试)或二进制
    • 多种语言可以读/写相同的消息(C++、Java、Python)

    【讨论】:

    • 这是一个有趣的概念,我可能会将它用于我的一些其他代码。
    【解决方案3】:

    很确定您必须实现自己的扩展才能实现这一点,我还没有看到也没有听说过任何这样的编译时构造。 MSVC 为预处理器提供__COUNTER__,但我知道没有等效模板。

    【讨论】:

    • 他已经实现了等效的模板,这很容易做到,而且这和__COUNTER__ 都无助于对文件/类进行排序。
    猜你喜欢
    • 2021-12-07
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多