我正在尝试编写一个程序,其中某些函数的名称取决于具有这样的宏的某个宏变量的值:

#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/

10-13 08:25