2 回答
TA贡献1982条经验 获得超2个赞
标准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);$
两级间接
为什么这需要两个层次的间接。轻率的答案是,因为这是标准要求它工作的方式;你往往会发现,你也需要与字符串操作符一样的技巧。
C99标准第6.10.3节涵盖“宏替换”,6.10.3.1涵盖“参数替换”。
在确定了调用类似于函数的宏的参数之后,就会发生参数替换。替换列表中的参数,除非前面有
#
或##
预处理令牌或后面是##
预处理令牌(见下文),在展开其中包含的所有宏后,将被相应的参数替换。在被替换之前,每个参数的预处理标记都会被宏完全替换,就好像它们构成了预处理文件的其余部分一样;没有其他的预处理标记可用。
在调用中NAME(mine)
,该参数为“my”;它被完全展开为“my”;然后将其替换为替换字符串:
EVALUATOR(mine, VARIABLE)
现在,宏评估器被发现,参数被隔离为‘my’和‘Variable’;后者随后被完全展开为‘3’,并被替换成替换字符串:
PASTER(mine, 3)
其他规则(6.10.3.3‘#操作符’)涵盖了这方面的操作:
如果在类似函数的宏的替换列表中,参数紧跟在
##
预处理令牌,将参数替换为相应参数的预处理令牌序列;[.]对于类似对象的宏调用和类似函数的宏调用,在重新检查替换列表以获得要替换的更多宏名称之前,
##
替换列表中的预处理令牌(不是从参数中删除)被删除,并将前面的预处理令牌与以下预处理令牌连接起来。
因此,替换列表包含x
紧随其后##
还有##
紧随其后y
因此,我们有:
mine ## _ ## 3
和消除##
任何一方的令牌和连接令牌将“my”与“_”和“3”组合在一起,从而产生如下结果:
mine_3
这是期望的结果。
如果我们看一下最初的问题,代码(修改为使用‘my’而不是‘SomeFunction’):
#define VARIABLE 3#define NAME(fun) fun ## _ ## VARIABLENAME(mine)
关于名字的论点显然是“我的”,而且已经完全扩展了。
按照6.10.3.3的规则,我们认为:
mine ## _ ## VARIABLE
当##
运算符被删除,映射到:
mine_VARIABLE
和问题中的报道完全一样。
传统C预处理器
对于没有令牌粘贴操作符的传统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" 2extern 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预处理器代替预标准处理器(通常有一种适当配置编译器的方法)可能比花很多时间想出一种方法来完成这一工作要简单得多。
TA贡献1828条经验 获得超6个赞
#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);
老实说,你不想知道为什么会这样。如果你知道为什么会起作用,你就会变成那个家伙在工作中,谁知道这类事情,每个人都会来问你问题。=)
编辑:如果你真的想知道它的工作原理,我很乐意发表一个解释,假设没有人比我强。
- 2 回答
- 0 关注
- 728 浏览
添加回答
举报