如何与 C 预处理器连接两次并扩展宏,如“arg ## _ ## MACRO"? [英] How can I concatenate twice with the C preprocessor and expand a macro as in "arg ## _ ## MACRO"?

查看:25
本文介绍了如何与 C 预处理器连接两次并扩展宏,如“arg ## _ ## MACRO"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to write a program where the names of some functions are dependent on the value of a certain macro variable with a macro like this:

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

int NAME(some_function)(int a);

不幸的是,宏 NAME() 把它变成了

Unfortunately, the macro NAME() turns that into

int some_function_VARIABLE(int a);

而不是

int some_function_3(int a);

所以这显然是错误的做法.幸运的是,VARIABLE 的不同可能值的数量很少,所以我可以简单地做一个 #if VARIABLE == n 并单独列出所有情况,但有没有聪明的方法来做到这一点?

so this is clearly the wrong way to go about it. Fortunately, the number of different possible values for VARIABLE is small, so I can simply do an #if VARIABLE == n and list all the cases separately, but is there is a clever way to do it?

推荐答案

标准 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 为什么这需要两级间接.轻率的答案是因为这就是标准要求它工作的方式;您往往会发现您也需要使用字符串化运算符的等效技巧.

Two levels of indirection

In a comment to another answer, Cade Roux asked why this needs two levels of indirection. The flippant answer is because that's how the standard requires it to work; you tend to find you need the equivalent trick with the stringizing operator too.

C99 标准的第 6.10.3 节涵盖宏替换",而 6.10.3.1 涵盖参数替换".

Section 6.10.3 of the C99 standard covers 'macro replacement', and 6.10.3.1 covers 'argument substitution'.

在确定调用类函数宏的参数后,发生参数替换.替换列表中的参数,除非在前面由 ### 预处理标记或后跟 ## 预处理标记(见下文),是在其中包含的所有宏都已被替换后由相应的参数替换扩大了.在被替换之前,每个参数的预处理标记是完全宏替换,就好像它们形成了预处理文件的其余部分;没有其他预处理令牌可用.

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';它完全扩展为我的";然后将其替换为替换字符串:

In the invocation NAME(mine), the argument is 'mine'; it is fully expanded to 'mine'; it is then substituted into the replacement string:

EVALUATOR(mine, VARIABLE)

现在宏 EVALUATOR 被发现,并且参数被隔离为 'mine' 和 'VARIABLE';后者然后完全扩展为 '3',并代入替换字符串:

Now the macro EVALUATOR is discovered, and the arguments are isolated as 'mine' and 'VARIABLE'; the latter is then fully expanded to '3', and substituted into the replacement string:

PASTER(mine, 3)

此操作由其他规则涵盖(6.10.3.3 'The ## operator'):

The operation of this is covered by other rules (6.10.3.3 'The ## operator'):

如果在类函数宏的替换列表中,参数紧跟在前面或者后面跟一个##预处理标记,参数替换为对应的参数的预处理标记序列;[...]

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;所以我们有:

So, the replacement list contains x followed by ## and also ## followed by y; so we have:

mine ## _ ## 3

并消除 ## 标记并连接任一侧的标记将mine"与_"和3"结合以产生:

and eliminating the ## tokens and concatenating the tokens on either side combines 'mine' with '_' and '3' to yield:

mine_3

这是想要的结果.

如果我们看一下原始问题,代码是(改编为使用mine"而不是some_function"):

If we look at the original question, the code was (adapted to use 'mine' instead of 'some_function'):

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

NAME(mine)

NAME 的论点显然是我的",并且已经完全展开.
按照6.10.3.3的规则,我们发现:

The argument to NAME is clearly 'mine' and that is fully expanded.
Following the rules of 6.10.3.3, we find:

mine ## _ ## VARIABLE

其中,当删除 ## 运算符时,映射到:

which, when the ## operators are eliminated, maps to:

mine_VARIABLE

正如问题中所报告的那样.

exactly as reported in the question.

罗伯特·鲁格 询问:

对于没有标记粘贴运算符 ## 的传统 C 预处理器,有什么办法可以解决这个问题吗?

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 Preprocessor 满足这个要求;XCode 8.2.1 中的 Clang 预处理器没有.

Maybe, and maybe not — it depends on the preprocessor. One of the advantages of the standard preprocessor is that it has this facility which works reliably, whereas there were different implementations for pre-standard preprocessors. One requirement is that when the preprocessor replaces a comment, it does not generate a space as the ANSI preprocessor is required to do. The GCC (6.3.0) C Preprocessor meets this requirement; the Clang preprocessor from XCode 8.2.1 does not.

当它工作时,它就完成了(x-paste.c):

When it works, this does the job (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 作为名称,当然在语法上是无效的.(现在,我可以把头发弄回来吗?)

Note that there isn't a space between fun, and VARIABLE — that is important because if present, it is copied to the output, and you end up with mine_ 3 as the name, which is not syntactically valid, of course. (Now, please can I have my hair back?)

使用 GCC 6.3.0(运行 cpp -traditional x-paste.c),我得到:

With GCC 6.3.0 (running cpp -traditional x-paste.c), I get:

# 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,我得到:

With Clang from XCode 8.2.1, I get:

# 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);

那些空间破坏了一切.我注意到两个预处理器都是正确的;不同的预标准预处理器表现出这两种行为,这使得在尝试移植代码时令牌粘贴成为一个非常烦人且不可靠的过程.带有 ## 符号的标准从根本上简化了这一点.

Those spaces spoil everything. I note that both preprocessors are correct; different pre-standard preprocessors exhibited both behaviours, which made token pasting an extremely annoying and unreliable process when trying to port code. The standard with the ## notation radically simplifies that.

可能还有其他方法可以做到这一点.但是,这不起作用:

There might be other ways to do this. However, this does not work:

#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 预处理器更简单花很多时间试图找出一种方法来完成这项工作.

Close, but no dice. YMMV, of course, depending on the pre-standard preprocessor that you're using. Frankly, if you're stuck with a preprocessor that is not cooperating, it would probably be simpler to arrange to use a standard C preprocessor in place of the pre-standard one (there is usually a way to configure the compiler appropriately) than to spend much time trying to work out a way to do the job.

这篇关于如何与 C 预处理器连接两次并扩展宏,如“arg ## _ ## MACRO"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆