如何使用具有可变数量参数的标记粘贴运算符?
How to use the token pasting operator with a variable number of arguments?
我想到了 #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's interesting P99 macro library includes the macro P99_PASTE
,它恰好具有您 concatenate
的签名,以及相同的语义。
至少可以说,P99 用来实现该功能的机制很复杂。特别是,它们依赖于数百个编号的宏,这些宏弥补了 C 预处理器不允许递归宏扩展的事实。
关于如何在 C 预处理器中进行迭代的另一个有用的解释可以在 Boost Preprocessor Library, particularly the topic on reentrancy.
的文档中找到。
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 标准没有对多个 ##
运算符施加任何特定的评估顺序。如果我们使用从右到左工作的预处理器,那么它会尝试做的第一件事就是粘贴 -
和 3
,这不会起作用,因为 -3
是不是一个单一的标记,就像更简单的定义一样。
现在,我不能提供一个编译器的例子,它会在这个宏上失败,因为我没有方便的从右到左的预处理器。 gcc 和 clang 都从左到右评估 ##
,我认为这是最常见的评估顺序。但是你不能依赖它;为了编写可移植代码,您需要确保按预期顺序评估粘贴运算符。这就是 P99_PASTE
.
提供的保证
注意: 可能有应用需要从右向左粘贴,但想了一段时间,唯一能来的例子从右到左而不是从左到右工作的令牌粘贴是以下相当晦涩的极端情况:
#define DOUBLE_HASH %: ## % ## :
而且我想不出任何可能出现的合理背景。
我想到了 #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's interesting P99 macro library includes the macro P99_PASTE
,它恰好具有您 concatenate
的签名,以及相同的语义。
至少可以说,P99 用来实现该功能的机制很复杂。特别是,它们依赖于数百个编号的宏,这些宏弥补了 C 预处理器不允许递归宏扩展的事实。
关于如何在 C 预处理器中进行迭代的另一个有用的解释可以在 Boost Preprocessor Library, particularly the topic on reentrancy.
的文档中找到。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 标准没有对多个 ##
运算符施加任何特定的评估顺序。如果我们使用从右到左工作的预处理器,那么它会尝试做的第一件事就是粘贴 -
和 3
,这不会起作用,因为 -3
是不是一个单一的标记,就像更简单的定义一样。
现在,我不能提供一个编译器的例子,它会在这个宏上失败,因为我没有方便的从右到左的预处理器。 gcc 和 clang 都从左到右评估 ##
,我认为这是最常见的评估顺序。但是你不能依赖它;为了编写可移植代码,您需要确保按预期顺序评估粘贴运算符。这就是 P99_PASTE
.
注意: 可能有应用需要从右向左粘贴,但想了一段时间,唯一能来的例子从右到左而不是从左到右工作的令牌粘贴是以下相当晦涩的极端情况:
#define DOUBLE_HASH %: ## % ## :
而且我想不出任何可能出现的合理背景。