【问题标题】:C++ construct that behaves like the __COUNTER__ macro [duplicate]行为类似于 __COUNTER__ 宏的 C++ 构造
【发布时间】:2014-06-06 01:26:06
【问题描述】:

我有一组 C++ 类,每个类都必须声明一个唯一的顺序 id 作为编译时常量。 为此,我使用了__COUNTER__ 内置宏,它转换为一个整数,每次出现时都会递增。 id 不需要遵循严格的顺序。唯一的要求是它们是顺序的,并且从 0 开始:

class A {
public:
    enum { id = __COUNTER__ };
};

class B {
public:
    enum { id = __COUNTER__ };
};

// etcetera ...

我的问题是:有没有办法使用 C++ 构造(例如模板)实现相同的结果?

【问题讨论】:

  • __COUNTER__ 有什么问题
  • 没有错,但如果有 C++ 的方式,我更愿意,以保持一致性。另外,我不确定__COUNTER__ 的便携性如何。
  • 可能不是,还有,你打算用它做什么?我很难看到宏跨库边界生成唯一 ID。
  • 我会说缺乏这样的结构是__COUNTER__存在的原因。
  • __COUNTER__ 在 Gnu CC (gcc) 和我在下面的@Cheers 评论中看到的,也在 Visual Studio 中得到支持——非常便携.. 除非你打算去一个非基于 linux 或 windows 的系统。

标签: c++ templates macros template-meta-programming


【解决方案1】:

这是一种使用__LINE__ 宏和模板的可能方法:

template <int>
struct Inc
{
    enum { value = 0 };
};

template <int index>
struct Id
{
    enum { value = Id<index - 1>::value + Inc<index - 1>::value };
};

template <>
struct Id<0>
{
    enum { value = 0 };
};

#define CLASS_DECLARATION(Class) \
template <> \
struct Inc<__LINE__> \
{ \
    enum { value = 1 }; \
}; \
 \
struct Class \
{ \
    enum { id = Id<__LINE__>::value }; \
private:

使用示例:

CLASS_DECLARATION(A)
    // ...
};

CLASS_DECLARATION(B)
    // ...
};

CLASS_DECLARATION(C)
    // ...
};

live example

【讨论】:

    【解决方案2】:

    显式链接是否可接受?

    class A {
    public:
        static const unsigned int id = 1;
    };
    
    class B {
    public:
        static const unsigned int id = A::id+1;
    };
    

    这种方法的优点是您始终获得相同的 ID,并且无论您的编译器是什么,您都知道它是什么。而使用__LINE____COUNTER__ 方法可能不是那么可预测的。缺点是链接你的类必须总是知道链上的前一个。

    使用模板(和 C++11):

    template <typename... T>
    class Identificable;
    
    template <>
    class Identificable<> {
    public:
        static const unsigned int id = 1;   
    };
    
    template <typename Prev>
    class Identificable<Prev> {
    public:
        static const unsigned int id = Prev::id+1;
    };
    
    class A : public Identificable<> {
    public:
    };
    
    class B : public Identificable<A> {
    public:
    };
    

    【讨论】:

    • 是的,谢谢@André。曾想过像这样的显式链接。如果您愿意添加一些耦合,这是一个有效的权衡。此外,它是一个确定性设置。对此 +1!
    【解决方案3】:

    标准 C++ 具有 __LINE__ 宏。

    也就是说,__LINE__ 按照要求是一个“C++ 构造”,而 __COUNTER__ 不是。


    根据您的需要,您可以简单地使用type_info 实例进行识别。 C++11 添加了对比较 type_info 的一般支持,称为 std::typeindex。这意味着您可以使用标准集合。

    【讨论】:

    • __LINE__ 是不可能的。我需要一个序号,因为我打算用它来索引一些数组。此外,它必须从一个已知值开始,最好是 0。对不起,如果我对“C++ 构造”不清楚。我的意思是C++独有的东西,比如模板。是的,宏是 C++ 的一部分,但它也是从 C 继承而来的。
    • @glampert:我认为您可以使用typeindex 作为std::map 的键。为此,您可以使用__LINE__ 数字作为键。但与typeindex 相比,如果类未在同一个翻译单元中定义,则可能难以确保它们是唯一的。
    • 是的,typeindex 看起来很整洁。这就是我投票的原因。但我想知道是否有某种方法可以使用模板元编程......
    【解决方案4】:

    有没有办法使用 C++ 构造实现相同的结果,例如 作为模板?

    是的,有 :-) 基本思想是使用链接分配的 ID,这样可以避免使用 __COUNTER____LINE__ 或之前提出的其他方法,并且不需要在类型定义中注入“额外”信息.

    这里简要描述了v1 中针对在 C++03 上实现的计数器使用模板元编程提出的解决方案。两个模板特化ID_by_TT_by_ID 用于在编译时定义链接type &lt;=&gt; ID。类型的 ID 是一个枚举常量。如果未定义链接,ID_by_T&lt;type&gt;::ID 返回 -1T_by_ID&lt;undefinedID&gt;::type 返回 null_t 预定义类型。宏DEF_TYPE_ID(type_name)type &lt;=&gt; ID 链接的定义处生成一个新的ID

    此方法基于宏重新定义:当使用#undef 未定义宏时,其值将扩展为 C++ 代码。例如:

    DEF_TYPE_ID(int)
    #undef  PREV_TYPE
    #define PREV_TYPE int
    

    DEF_TYPE_ID 使用以下对宏PREV_TYPE 的先前定义的调用:ID_T_pair&lt;type_name, ID_by_T&lt;PREV_TYPE&gt;::ID+1&gt;。这就是为什么我说链接分配的 ID。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多