我正在尝试编写一个程序,其中某些函数的名称取决于具有这样的宏的某个宏变量的值:
#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
并单独列出所有情况,但是我想知道是否有一种聪明的方法来做到这一点。 最佳答案
标准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 asked为什么需要两个间接级别。坦率的答案是因为这就是标准要求它起作用的方式。您往往会发现您也需要使用字符串化运算符的等效技巧。
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
并消除
##
token ,并在任一侧将 token 串联在一起,将'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预处理器
Robert Rüger asks:
也许,也许不是,这取决于预处理器。标准预处理器的优点之一是它具有可靠运行的功能,而标准预处理器有不同的实现方式。一个要求是,当预处理器替换注释时,它不会像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);
这些空间破坏了一切。我注意到两个预处理器都是正确的。不同的预标准预处理器都表现出两种行为,这使得 token 标记在尝试移植代码时变得非常烦人且不可靠。带有
##
表示法的标准从根本上简化了这一过程。可能还有其他方法可以做到这一点。但是,这不起作用:
#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预处理器来代替标准前者可能会更简单(通常有一种适本地配置编译器的方法),而不是花很多时间试图找到一种完成工作的方法。
关于c - 如何像 "arg ## _ ## MACRO"一样用C预处理器连接两次并扩展宏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33856840/