如何使用带有可变数量参数的令牌粘贴运算符?

问题描述 投票:1回答:1

我想到了#define concatenate(a, b, c) a ## b ## c的通用版本

我这样尝试过:

#include <stdio.h>

#define concatenate(arg1, ...) arg1 ## __VA_ARGS__

int main()
{
    int dob = 121201;
    printf("%d", concatenate(d, o, b));

    return 0;
}

我也尝试了许多其他方式:

#define concatenate(arg1, ...) arg1 ## ##__VA_ARGS__

#define concatenate(...) ## ##__VA_ARGS__

#define concatenate(...) ##__VA_ARGS__

#define concatenate(arg1, ...) arg1 ## ...

#define concatenate(arg1, ...) arg1 ## concatenate(##__VA_ARGS__)

A,我所有的尝试都失败了。我想知道是否有可能以任何方式做到这一点?

c c-preprocessor variadic-macros token-pasting-operator
1个回答
0
投票

有可能。 Jens Gustedt有趣的P99 macro library包含宏P99_PASTE,该宏具有您P99_PASTE的准确签名,并且具有相同的语义。

至少可以说,P99用于实现该功能的机制很复杂。特别是,它们依赖于数百个编号的宏,这些宏可以弥补C预处理器不允许递归宏扩展的事实。

[在concatenate的文档中,特别是在Boost Preprocessor Library的主题中,找到了有关如何在C预处理器中进行迭代的另一种有用的解释。

[Jens关于reentrancy的文档强调了以下事实:宏从左到右粘贴,以避免P99_PASTE的歧义。这可能需要一些解释。

令牌粘贴(##)运算符是二进制运算符;如果要将两个以上的片段粘贴到单个标记中,则需要一次进行一对标记,这意味着所有中间结果必须是有效的标记。这可能需要一定的注意。例如,考虑以下宏,它试图将指数添加到整数的末尾:

##

(这仅在两个宏参数都是文字整数的情况下才起作用。为了使宏参数成为宏,我们需要在宏扩展中引入另一种间接层。但这不是重点。)

我们几乎会立即发现,#define EXPONENT(INT, EXP) INT ## E ## EXP 不起作用,因为EXPONENT(42,-3)不是单个标记。这是两个标记,-3-,粘贴操作符将仅粘贴3。这将导致两个令牌序列- 42E-,最终将导致编译器错误。

顺便说一下,

342E是有效的令牌。它们是ppnumbers(预处理数字),它是点,数字,字母和指数的任意组合,条件是令牌以数字或点开头,后跟数字。 (指数是字母42E-E之一,可能是小写字母,并且可能后跟一个符号。否则,符号字符不能出现在ppnumber中。)

因此我们可以通过要求用户将符号与数字分开来解决此问题:

P

如果从左到右评估#define EXPONENT(INT, SIGN, EXP) INT ## E ## SIGN ## EXP EXPONENT(42,-,3) 运算符,则将起作用。但是C标准没有对多个##运算符强加任何特定的评估顺序。如果我们使用的是从右到左工作的预处理器,那么它将尝试做的第一件事是粘贴##-,这将不起作用,因为3不是单个标记,只是和更简单的定义一样。

现在,我无法提供在此宏上失败的编译器示例,因为我没有从右到左的预处理器。 gcc和clang都从左到右评估-3,我认为这是最常见的评估顺序。但是你不能依靠它。为了编写可移植的代码,您需要确保按预期顺序评估粘贴操作符。这就是##提供的保证。

Note:可能有一个应用程序需要从右到左粘贴,但是考虑了一段时间后,我可以想到的唯一示例就是可以工作的令牌粘贴从右到左而不是从左到右是以下相当晦涩的拐角案例:

P99_PASTE

而且我想不出任何可能出现的情况。

© www.soinside.com 2019 - 2024. All rights reserved.