标准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);
$
二级间接
在对另一个答案的评论中,Cade Roux 问为什么这需要两个级别的间接访问。坦率的答案是因为这就是标准要求它起作用的方式。您可能会发现您也需要使用字符串化运算符的等效技巧。
C99标准的6.10.3节涵盖了“宏替换”,而6.10.3.1节则涵盖了“参数替换”。
在确定了调用类似函数的宏的参数之后,将进行参数替换。除非包含在其中的所有宏都已被扩展,否则替换列表中的参数(除非前面带有#
或##
预处理令牌或后面带有##
预处理令牌(请参见下文))被相应的参数替换。在被替换之前,每个参数的预处理令牌都被完全替换为宏,就好像它们构成了其余的预处理文件一样。没有其他预处理令牌可用。
在调用中NAME(mine)
,参数为'mine';它已完全扩展为“我的”;然后将其替换为替换字符串:
EVALUATOR(mine, VARIABLE)
现在发现了宏EVALUATOR,并且参数被隔离为“ mine”和“ VARIABLE”;然后将后者完全扩展为'3',并替换为替换字符串:
PASTER(mine, 3)
其他规则(6.10.3.3'##运算符')涵盖了此操作:
如果在类似函数的宏的替换列表中,参数紧跟在##
预处理标记之前或之后,则该参数将被相应参数的预处理标记序列替换;[...]
对于类对象和类函数宏调用,在重新检查替换列表以查找更多要替换的宏名称之前,将##
删除替换列表中的预处理令牌的每个实例(而不是从参数中获取),并连接前面的预处理令牌使用以下预处理令牌。
因此,更换列表包含x
后跟##
也##
随后y
; 所以我们有:
mine ## _ ## 3
并消除##
令牌并在任一侧串联令牌,将'mine'与'_'和'3'结合起来得到:
mine_3
这是期望的结果。
如果我们看原始问题,代码是(适合使用“我的”而不是“ some_function”):
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
NAME的论点显然是“我的”,并且已得到充分扩展。
遵循6.10.3.3的规则,我们发现:
mine ## _ ## VARIABLE
##
消除了运算符后,它们映射到:
mine_VARIABLE
完全与问题中所报告的一样。
传统C预处理器
罗伯特·吕格(RobertRüger) 问:
没有令牌粘贴运算符的传统C预处理器有什么办法做到这一点##
?
也许,也许不是,这取决于预处理器。标准预处理器的优点之一是它具有可靠运行的功能,而标准预处理器有不同的实现方式。一个要求是,当预处理器替换注释时,它不会像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);
这些空间破坏了一切。我注意到两个预处理器都是正确的。不同的预标准预处理器都表现出两种行为,这使得令牌标记在尝试移植代码时非常烦人且不可靠。带有##
符号的标准从根本上简化了这一过程。
可能还有其他方法可以做到这一点。但是,这不起作用:
#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);
接近,但没有骰子。当然,YMMV取决于您使用的标准预处理器。坦白说,如果您坚持使用不合作的预处理器,那么安排使用标准C预处理器来代替标准前者可能会更简单(通常有一种适当配置编译器的方法),而不是花很多时间试图找到一种完成工作的方法。