将宏名称传递到X宏列表是否合法

时间:2019-07-18 05:39:14

标签: c++ c c-preprocessor x-macros

我想到以下是X-macro技巧的首选样式:

#define LIST_OF_COLOURS(X) \
    X(RED) \
    X(GREEN) \
    X(BLUE)

#define LIST_OF_FRUIT(X) \
    X(APPLE) \
    X(ORANGE) \
    X(TOMATO)

具体来说,将X宏传递给列表,而不是每次实例化列表时都对其进行未定义和重新定义。这样可以:

#define X_LIST(x) x,
#define X_STRING_LIST(x) #x,
#define COMPREHENSIVE_SETUP(n, l)  \
    enum n { l(X_LIST) };  \
    char const* n##Names[] = { l(X_STRING_LIST) };

COMPREHENSIVE_SETUP(Colour, LIST_OF_COLOURS)
COMPREHENSIVE_SETUP(Fruit, LIST_OF_FRUIT)

但是问题是我没有经常在野外看到这种习语,这不是Wikipedia所描述的,即使我每次尝试并感觉更加方便时,它似乎都可以工作。

我的问题是,这实际上合法且已完全定义,还是我依赖​​未定义的行为?

1 个答案:

答案 0 :(得分:5)

是的,这是有效的。在C标准中,§6.10.3宏替换对宏之类的功能进行了预处理。相关部分如下:

  

¶10 ...后面跟随的每个类似于函数的宏名称的后续实例   由(组成,因为下一个预处理令牌介绍了   被替换列表中的替换列表替换的预处理令牌   定义(宏的调用)。...

     

6.10.3.1参数替换

     

¶1在调用类似函数的宏的参数之后   确定后,将进行参数替换。一个参数   在替换列表中,除非在#或##预处理之前   令牌或后面跟有##预处理令牌(请参见下文)被替换   在其中包含的所有宏都具有   已扩展。在替换之前,每个参数的预处理   标记完全被宏替换,就好像它们构成了其余的   预处理文件;没有其他预处理令牌可用。

     

6.10.3.4重新扫描并进一步替换

     

¶1替换了替换列表中的所有参数之后   和#和##处理已经进行,所有地标标记都已预处理   令牌已删除。然后生成的预处理令牌序列   重新扫描,以及所有后续的预处理标记   源文件,以替换更多的宏名称。

除了节名称和编号外,C ++标准中也存在相同的措辞。

因此,当您插入X_LIST时,preprcoessor将在尝试扩展X后将其替换为X_LIST,就好像它是一个像宏一样的对象。由于不是,X剩下的令牌是X_LIST

然后预处理器再次扫描该行。这次X_LIST之后是(,因此将 now 展开。

将宏名称之类的函数传递给“高阶函数”并非闻所未闻。 Boost.Preprocessor库大量使用了这个习惯用法。