我想到了#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__)
唉,我所有的尝试都失败了。我想知道是否有可能以任何方式做到这一点?
这是可能的。Jens Gustedt有趣的P99宏库包括宏P99_PASTE
,它正好具有concatenate
的签名,以及相同的语义。
P99用来实现该功能的机制至少可以说是复杂的。特别是,它们依赖于几百个编号的宏,这弥补了C预处理器不允许递归宏扩展的事实。
关于如何在C预处理器中进行迭代的另一个有用的解释可以在Boost预处理器库的文档中找到,特别是关于可重入性的主题。
Jens的P99_PASTE
文档强调了宏从左到右粘贴的事实,以避免##
的歧义。这可能需要一点解释。
标记粘贴(##
(运算符是一个二进制运算符;如果要将两个以上的段粘贴到一个令牌中,则需要一次粘贴一对,这意味着所有中间结果都必须是有效的令牌。这可能需要一定程度的谨慎。例如,考虑这个宏,它试图将指数添加到整数的末尾:
#define EXPONENT(INT, EXP) INT ## E ## EXP
(只有当两个宏参数都是文字整数时,这才有效。为了允许宏参数是宏,我们需要在宏扩展中引入另一个间接级别。但这不是重点。(
我们几乎会立即发现EXPONENT(42,-3)
不起作用,因为-3
不是一个单独的令牌。它是两个令牌,-
和3
,粘贴操作符将只粘贴-
。这将导致一个双令牌序列42E-
3
,这将最终导致编译器错误。
42E
和42E-
是有效的令牌。它们是ppnumbers,预处理数字,是点、数字、字母和指数的任何组合,前提是令牌以数字或点后跟数字。(指数是字母E
或P
之一,可能是小写,后面可能跟一个符号。否则,符号字符不能出现在ppnumber中。(
因此,我们可以尝试通过要求用户将符号与数字分开来解决这个问题:
#define EXPONENT(INT, SIGN, EXP) INT ## E ## SIGN ## EXP
EXPONENT(42,-,3)
如果从左到右对##
运算符进行求值,则会起作用。但C标准没有对多个CCD_ 24算子施加任何特定的评估顺序。如果我们使用的是从右到左工作的预处理器,那么它要做的第一件事就是粘贴-
和3
,这不会起作用,因为-3
不是一个单一的令牌,就像简单的定义一样。
现在,我不能提供一个编译器在这个宏上失败的例子,因为我手边没有从右到左的预处理器。gcc和clang都从左到右评估##
,我认为这是最常见的评估顺序。但你不能依赖这一点;为了编写可移植代码,您需要确保按照预期的顺序评估粘贴运算符。这就是P99_PASTE
提供的担保。
注意:可能有一个应用程序需要从右向左粘贴,但经过一段时间的思考,我能想到的唯一一个从右向左但不从左向右的令牌粘贴示例是以下相当模糊的角情况:
#define DOUBLE_HASH %: ## % ## :
我想不出任何可能出现这种情况的合理背景。