我想到以下是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所描述的,即使我每次尝试并感觉更加方便时,它似乎都可以工作。
我的问题是,这实际上合法且已完全定义,还是我依赖未定义的行为?
答案 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库大量使用了这个习惯用法。