如何与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);
老实说,你不想知道为什么这个工程。 如果你知道它为什么起作用,你就会成为那个知道这种事情的人,每个人都会问你一些问题。 =)
编辑:如果你真的想知道为什么它的作品,我会愉快地发表一个解释,假设没有人打我。
- validation从移动(iPhone)应用程序到ASP.Net Web API的请求(请在我的devise中提供反馈)
- Jenkins用户身份validation细节如何被“传递”给使用Jenkins API创build作业的脚本?
- 包含令牌参数的https URL:它有多安全?
- 如何在Android中安全地存储访问令牌和秘密?
- 用于Java的JWT(JSON Web令牌)库
- 错误:未捕获的SyntaxError:意外的标记<
- 在C中嵌套strtok函数问题
- 为忘记密码生成随机令牌的最佳实践
- 无法从Facebook获取访问令牌。 有一个OAuthException说:“错误validationvalidation码”