gpt4 book ai didi

c - 如何与 C 预处理器连接两次并展开宏,如 "arg ## _ ## MACRO"中所示?

转载 作者:行者123 更新时间:2023-11-30 17:00:13 25 4
gpt4 key购买 nike

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

#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 节涵盖“参数替换”。

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

在调用NAME(mine)中,参数是“mine”;它完全扩展到“我的”;然后将其替换为替换字符串:

EVALUATOR(mine, VARIABLE)

现在发现了宏 EVALUATOR,并且参数被隔离为“我的”和“变量”;然后后者完全扩展为“3”,并替换为替换字符串:

PASTER(mine, 3)

其他规则涵盖了此操作(6.10.3.3“## 运算符”):

If, in the replacement list of a function-like macro, a parameter is immediately preceded or followed by a ## preprocessing token, the parameter is replaced by the corresponding argument’s preprocessing token sequence; [...]

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token.

因此,替换列表包含 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 预处理器

Robert Rüger asks :

Is there any way do to this with the traditional C preprocessor which does not have the token pasting operator ##?

也许,也许不是——这取决于预处理器。标准预处理器的优点之一是它具有可靠工作的功能,而预标准预处理器有不同的实现。一项要求是,当预处理器替换注释时,它不会像 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 预处理器更简单(通常有一种方法可以适当配置编译器)。花费大量时间试图找出完成这项工作的方法。

关于c - 如何与 C 预处理器连接两次并展开宏,如 "arg ## _ ## MACRO"中所示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37722382/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com