我正在尝试编写一个程序,其中一些函数的名称依赖于某个宏变量的值,宏如下:
#define VARIABLE 3 #define NAME(fun) fun ## _ ## VARIABLE int NAME(some_function)(int a);
不幸的是,宏NAME()
将其转化为
int some_function_VARIABLE(int a);
而不是
int some_function_3(int a);
所以这显然是错误的方式.幸运的是,VARIABLE的不同可能值的数量很小所以我可以简单地做一个#if VARIABLE == n
并单独列出所有情况,但我想知道是否有一个聪明的方法来做到这一点.
$ 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 "" # 1 " " # 1 "xx.c" extern void mine_3(char *x); $
在对另一个答案的评论中,Cade Roux 问为什么这需要两个间接层次.轻率的回答是因为这就是标准要求它起作用的方式; 你倾向于发现你需要与字符串化运算符相同的技巧.
C99标准的6.10.3节涵盖了"宏观替代",6.10.3.1涵盖了"论证替代".
在确定了调用类函数宏的参数之后,发生了参数替换.在替换列表中的参数,除非前面有一个
#
或##
预处理记号或后跟一个##
预处理标记(见下文),由对应的参数替换后其中所含的所有宏已经扩大.在被替换之前,每个参数的预处理标记都被完全宏替换,好像它们形成了预处理文件的其余部分; 没有其他预处理令牌可用.
在调用中NAME(mine)
,参数是'我的'; 它完全扩展到'我的'; 然后将其替换为替换字符串:
EVALUATOR(mine, VARIABLE)
现在发现宏EVALUATOR,参数被隔离为'mine'和'VARIABLE'; 然后后者完全扩展为'3',并替换为替换字符串:
PASTER(mine, 3)
其他规则(6.10.3.3'## operator')涵盖了此操作:
如果在类函数宏的替换列表中,参数紧跟在
##
预处理标记之前或之后,则该参数将被相应参数的预处理标记序列替换; [...]对于类似对象和类似函数的宏调用,在重新检查替换列表以替换更多宏名称之前,
##
删除替换列表中的预处理标记的每个实例(不是来自参数),并且连接前面的预处理标记使用以下预处理标记.
因此,替换列表包含x
后跟##
,也##
包括y
; 所以我们有:
mine ## _ ## 3
并且消除##
令牌并连接两侧的令牌将'mine'与'_'和'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
正如问题中所报道的那样.
RobertRüger 问道:
对于没有令牌粘贴操作符的传统C预处理器,有没有办法做到这一点
##
?
也许,也许不是 - 这取决于预处理器.标准预处理器的一个优点是它具有可靠的工作,而预标准预处理器则有不同的实现.一个要求是当预处理器替换注释时,它不会生成空间,因为ANSI预处理器需要这样做.GCC(6.3.0)C预处理器符合此要求; XCode 8.2.1的Clang预处理器没有.
当它工作时,这就完成了job(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 "" # 1 " " # 1 "x-paste.c" extern void mine_3(char *x);
使用来自XCode 8.2.1的Clang,我得到:
# 1 "x-paste.c" # 1 "" 1 # 1 " " 3 # 329 " " 3 # 1 " " 1 # 1 " " 2 # 1 "x-paste.c" 2 extern void mine _ 3(char *x);
那些空间破坏了一切.我注意到两个预处理器都是正确的; 不同的预标准预处理器展示了这两种行为,这使得在尝试移植代码时令牌粘贴成为一个非常烦人且不可靠的过程.带##
符号的标准从根本上简化了这一点.
可能还有其他方法可以做到这一点.但是,这不起作用:
#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 "" # 1 " " # 1 "x-paste.c" extern void mine_VARIABLE(char *x);
关闭,但没有骰子.YMMV,当然,取决于您正在使用的预标准预处理器.坦率地说,如果你坚持使用不合作的预处理器,那么安排使用标准C预处理器代替预先标准的预处理器(通常有一种方法可以适当地配置编译器)可能更简单.花很多时间试图找到一种方法来完成这项工作.
#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);
老实说,你不想知道为什么会这样.如果你知道它为什么会起作用,那么你就会成为那个知道这类事情的人,每个人都会问你问题.=)
编辑:如果你真的想知道它为什么会起作用,我会高兴地发布一个解释,假设没有人打败我.