如何与C预处理器连接两次,并在“arg ## _ ## MACRO”中展开macros?

我试图编写一个程序,其中一些函数的名称依赖于某个macrosvariables的值,如下所示:

#define VARIABLE 3 #define NAME(fun) fun ## _ ## VARIABLE int NAME(some_function)(int a); 

不幸的是,macrosNAME()将其转化为

 int some_function_VARIABLE(int a); 

而不是

 int some_function_3(int a); 

所以这显然是错误的做法。 幸运的是,VARIABLE的不同可能值的数量很小,所以我可以简单地做一个#if VARIABLE == n并分别列出所有的情况,但是我想知道是否有一个聪明的方法来做到这一点。

标准C预处理器

 $ cat xx.c #define VARIABLE 3 #define PASTER(x,y) x ## _ ## y #define EVALUATOR(x,y) PASTER(x,y) #define NAME(fun) EVALUATOR(fun, VARIABLE) extern void NAME(mine)(char *x); $ gcc -E xx.c # 1 "xx.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "xx.c" extern void mine_3(char *x); $ 

两个层面的间接

在对另一个答案的评论中, 凯德鲁克斯 问为什么这需要两个层面的间接。 轻浮的答案是因为这就是标准要求它工作的原因; 你倾向于发现你需要与string操作符相同的技巧。

C99标准第6.10.3节涵盖“macros观替代”,6.10.3.1涵盖“替代”。

在调用类似函数macros的参数被识别之后,参数replace就会发生。 replace列表中的参数(除非前面有###预处理标记或后面跟着##预处理标记(请参见下文))在扩展了所有macros后,将被相应的参数replace。 在被replace之前,每个参数的预处理标记完全被macros代替,就像它们构成了预处理文件的其余部分一样; 没有其他预处理令牌可用。

在调用NAME(mine) ,论点是“我的”。 它完全扩展到“我的”; 它被replace成replacestring:

 EVALUATOR(mine, VARIABLE) 

现在macros观评估者被发现,论证被孤立为“我的”和“可变的”; 后者则完全展开为'3',并代入replacestring:

 PASTER(mine, 3) 

其他规则(6.10.3.3'##运算符')涵盖了这个操作:

如果在一个类似函数的macros的replace列表中,一个参数紧接着一个##预处理标记的前面或后面,那么这个参数将被相应的参数的预处理标记序列replace; […]

对于类似于对象和类似函数的macros调用,在对replace列表进行重新检查以replace更多macros名称之前,删除replace列表(不是来自参数)中的##预处理标记的每个实例,并删除前面的预处理标记与以下预处理令牌连接。

所以,replace列表包含x后面跟着## ,还有##后面跟着y ; 所以我们有:

 mine ## _ ## 3 

并消除##令牌,并将两侧的令牌结合起来,将“我的”与“_”和“3”结合起来,得到:

 mine_3 

这是理想的结果。


如果我们看一下原来的问题,那么代码就是(适应于使用'mine'而不是'some_function'):

 #define VARIABLE 3 #define NAME(fun) fun ## _ ## VARIABLE NAME(mine) 

对NAME的争论显然是“我的”,而且已经完全展开了。
遵循6.10.3.3的规则,我们发现:

 mine ## _ ## VARIABLE 

##运算符被消除时,映射到:

 mine_VARIABLE 

正如问题中所报告的那样。


传统的C预处理器

罗伯特·Rüger 问 :

有没有办法做到这一点与传统的C预处理器没有令牌粘贴操作符##

也许,也许不是 – 这取决于预处理器。 标准预处理器的优点之一是它具有可靠工作的function,而预标准预处理器有不同的实现。 一个要求是当预处理器replace注释时,它不会像ANSI预处理器那样产生空间。 GCC(6.3.0)C预处理器满足这个要求; 来自XCode 8.2.1的Clang预处理器没有。

当它工作,这个工作( x-paste.c ):

 #define VARIABLE 3 #define PASTE2(x,y) x/**/y #define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y) #define NAME(fun) EVALUATOR(fun,VARIABLE) extern void NAME(mine)(char *x); 

请注意, fun,VARIABLE之间没有空格 – 这一点很重要,因为如果存在,它将被复制到输出中,并以mine_ 3作为名称,当然,这在语法上不是有效的。 (现在,请把我的头发回来?)

使用GCC 6.3.0(运行cpp -traditional x-paste.c ),我得到:

 # 1 "x-paste.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "x-paste.c" extern void mine_3(char *x); 

来自XCode 8.2.1的Clang,我得到:

 # 1 "x-paste.c" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 329 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "x-paste.c" 2 extern void mine _ 3(char *x); 

那些空间破坏了一切。 我注意到两个预处理器都是正确的。 不同的预标准预处理器performance出这两种行为,这使得令牌在尝试移植代码时粘贴非常烦人和不可靠的过程。 带##符号的标准从根本上简化了这一点。

有可能有其他的方法来做到这一点。 但是,这不起作用:

 #define VARIABLE 3 #define PASTER(x,y) x/**/_/**/y #define EVALUATOR(x,y) PASTER(x,y) #define NAME(fun) EVALUATOR(fun,VARIABLE) extern void NAME(mine)(char *x); 

GCC生成:

 # 1 "x-paste.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "x-paste.c" extern void mine_VARIABLE(char *x); 

closures,但没有骰子。 YMMV,当然,取决于你使用的预标准预处理器。 坦率地说,如果你遇到了一个不合作的预处理器,那么安排使用一个标准的C预处理器来取代预标准的预处理器可能会更简单一些(通常有一种方法来configuration编译器)花很多时间去尝试找出一种方法来完成这项工作。

 #define VARIABLE 3 #define NAME2(fun,suffix) fun ## _ ## suffix #define NAME1(fun,suffix) NAME2(fun,suffix) #define NAME(fun) NAME1(fun,VARIABLE) int NAME(some_function)(int a); 

老实说,你不想知道为什么这个工程。 如果你知道它为什么起作用,你就会成为那个知道这种事情的人,每个人都会问你一些问题。 =)

编辑:如果你真的想知道为什么它的作品,我会愉快地发表一个解释,假设没有人打我。