【问题标题】:Macro generating a valid identifier for template-class static instance为模板类静态实例生成有效标识符的宏
【发布时间】:2017-02-27 10:07:44
【问题描述】:

背景

我正在修改 C++ 中的多态序列化和反序列化。 为此,我使用静态地图:[type id-string] -> [type factory function]。 每种类型都必须在此映射中注册,我想在编译时进行。

方法

天真的方法是:

/// Creates a concrete serializable type provided as a template parameter
template <typename T>
ISerializable* createSerializable() { return new T; }

/// Factory that registers a serializable type T
template <typename T>
struct SerializableFactory
{
    SerializableFactory(const char* type_name)
    {
        // registerType adds a type_name->factory_function entry to the map
        registerType(type_name, createSerializable<T>);
    }
};

注册类型是通过宏来完成的:

/// Macro that registers the type at compile-time using a static factory instance
#define REGISTER_TYPE(T)                                                   \
    static SerializableFactory<T> global_##T##Factory(#T);

例如REGISTER_TYPE(ArbitraryClass)会变成:

static SerializableFactory<ArbitraryClass> 
    global_ArbitraryClassFactory("ArbitraryClass");

问题

很遗憾,这不适用于ArbitraryClass&lt;int&gt;,因为&lt;&gt; 不允许在标识符中使用。

问题

有没有很好的解决方法来实现以这种方式注册任意模板类型?

替代品

我考虑了以下替代方案(每个都有缺点):

  • 在运行时注册类型:看起来不那么优雅,需要序列化用户更多的努力;
  • RTTI:需要启用 RTTI,不保证不同类型总是有不同的哈希/名称(当然不太可能,但仍然如此);
  • 要求用户提供类型别名或别名:不太优雅,序列化用户需要付出更多努力

更新:

  • 正如@Svalorzen 在他的回答中提到的那样,匿名命名空间 可以使用。不幸的是,不能在翻译单元中多次使用宏是一个缺点。例如,对于仅标题类型。

【问题讨论】:

  • 您是否考虑过使用__COUNTER__ 宏而不是T 来生成工厂实例唯一名称?我知道这不是标准的,但绝对可以在这里解决问题......
  • @W.F.不,我没有。谢谢你的建议。是的,不标准是一个缺点。如果没有副作用,并且被三大编译器支持,谁知道:)

标签: c++ c++11 templates macros


【解决方案1】:

部分解决方案是始终使用相同的名称,但将其包装在未命名的命名空间中。这只允许您为每个翻译单元注册一个类型,但也许这已经足够了。

/// Macro that registers the type at compile-time using a static factory instance
#define REGISTER_TYPE(T)                                         \
    namespace {                                                  \
        static SerializableFactory<T> serial_global_factory(#T); \
    }

否则,您可以使用 __LINE____FILE__ 宏标记为您的对象创建一个唯一的名称 - 只要您不必在其他任何地方引用它。还有其他的,列表可以找到here

【讨论】:

  • 感谢您的回答。对不起,我忘了在我的问题中提到这个替代方案。我会延长它。是的,不能在同一个翻译单元中使用宏对用户来说是相当不利的。例如,仅标头类型。
  • @AMA 另一个选项,我已添加到我的答案中,类似于 W.F.也就是说,是使用任何允许您创建唯一标识符的预处理器令牌。
  • 感谢您的建议。我在想。 __FILE__ 宏不是很有用,因为它可以包含任意符号。但后来它击中了我:未命名的命名空间 + __LINE__ 可以做到。我已经添加了我自己的答案。但是,如果您觉得这是您的建议,请扩展您的答案,我会很乐意接受它:)
  • @AMA 取决于您需要对该符号执行的操作。如果只是添加一个钩子来注册一个类型,它的名字是什么都无所谓。而且无论如何,如果您以后需要再次使用它,您还可以添加一个指向它的指针到您的地图以找到它。
  • 是的,就我而言,我只需要注册类型。
【解决方案2】:

我有一个 aha! 时刻,灵感来自 @W.F. 的评论。 ,@Svalorzen 答案和this 答案。我相信这是一个相当聪明的把戏,没有任何替代品的缺点。


解决方案:使用未命名/匿名命名空间__LINE__ 添加到标识符应始终提供唯一标识符(除非宏在同一行使用两次)。


它的外观如下:

#define MERGE(A, B) A##B
#define CREATE_UNIQUE_IDENTIFIER(line) MERGE(unique_identifier_on_line_, line)
/// UNIQUE_IDENTIFIER generates identifiers like:
/// "unique_identifier_on_line_" + line_number
#define UNIQUE_IDENTIFIER CREATE_UNIQUE_IDENTIFIER(__LINE__)
/// Macro that registers the type at compile-time using a static factory instance
#define REGISTER_TYPE(T)                                                   \
    namespace                                                              \
    {                                                                      \
    static SerializableFactory<T> UNIQUE_IDENTIFIER(#T);                   \
    }

【讨论】:

    【解决方案3】:

    尝试将类型定义为单个名称并使用名称:

    typedef ArbitraryClass<int> ArbitraryClass_int_;
    REGISTER_TYPE(ArbitraryClass_int_);
    

    您也可以尝试将其放入哈希映射中,其中键是类型名。

    【讨论】:

    • 感谢您的回答。但你的建议就是我的意思是要求用户提供别名替代。
    • 你也会遇到问题,如果你在模板中定义它们,导致字符串总是"T"typeid(T).name() 得到一个真正的单独的字符串,但要缩短。您可以将其扩展到库函数的表示。如果您想让它更动态,请尝试使用 preprogressor,例如。用python写的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-11
    • 2011-01-14
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 2017-10-03
    • 1970-01-01
    相关资源
    最近更新 更多