【问题标题】:How to use the token pasting operator with a variable number of arguments?如何使用带有可变数量参数的标记粘贴运算符?
【发布时间】:2020-04-04 14:34:58
【问题描述】:

我想有一个通用版本的#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__)

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

【问题讨论】:

    标签: c c-preprocessor variadic-macros token-pasting-operator


    【解决方案1】:

    这是可能的。 Jens Gustedt 的有趣 P99 macro library 包含宏 P99_PASTE,它具有您的 concatenate 的签名,以及相同的语义。

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

    Boost Preprocessor Library 的文档中可以找到另一个关于如何在 C 预处理器中进行迭代的有用说明,尤其是关于 reentrancy 的主题。

    Jens 的P99_PASTE 文档强调了宏从左到右粘贴以避免## 的歧义这一事实。这可能需要一点解释。

    token-paste (##) 运算符是二元运算符;如果要将两个以上的段粘贴到单个标记中,则需要一次执行一对,这意味着所有中间结果必须是有效标记。这可能需要一定程度的谨慎。例如,考虑这个试图在整数末尾添加指数的宏:

    #define EXPONENT(INT, EXP) INT ## E ## EXP
    

    (这仅在两个宏参数都是字面整数时才有效。为了允许宏参数是宏,我们需要在宏扩展中引入另一层间接性。但这不是重点。)

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

    42E42E- 是有效的令牌,顺便说一下。它们是 ppnumbers,预处理数字,可以是点、数字、字母和指数的任意组合,前提是令牌以数字开头或点后跟数字。 (指数是EP 中的一个字母,可能是小写,后面可能跟一个符号。否则,符号字符不能出现在ppnumber中。)

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

    #define EXPONENT(INT, SIGN, EXP) INT ## E ## SIGN ## EXP
    
    EXPONENT(42,-,3)
    

    如果## 运算符是从左到右计算的,那么这将起作用。但是 C 标准并没有对多个 ## 运算符施加任何特定的评估顺序。如果我们使用从右到左工作的预处理器,那么它首先会尝试粘贴-3,这将不起作用,因为-3 不是单个标记,只是与更简单的定义一样。

    现在,我不能提供一个编译器会在这个宏上失败的例子,因为我手边没有从右到左的预处理器。 gcc 和 clang 都评估 ## 从左到右,我认为这绝对是最常见的评估顺序。但是你不能依赖它;为了编写可移植的代码,您需要确保以预期的顺序评估粘贴操作符。这就是P99_PASTE提供的保证。

    注意:可能有一个应用程序需要从右到左粘贴,但考虑了一段时间后,我能想出的唯一例子是token可以从右到左但不能从左到右工作的粘贴是以下相当模糊的极端情况:

    #define DOUBLE_HASH %: ## % ## :
    

    我想不出任何可能出现这种情况的合理背景。

    【讨论】:

    • 老实说,当预处理器不支持递归时,我不明白可变参数宏的用途:它会导致非常复杂的解决方法或以丑陋的级联方式实现宏,最大限制参数的数量(如 NARGS 技巧,通常限制为 64 个参数)......因此,您不仅编写了一个长而丑陋的宏级联,甚至还限制了参数的数量,从而打破了可变参数范式。恕我直言,可变参数宏是不能使用的,因为预处理器不够强大。
    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 2012-03-28
    • 2012-11-22
    • 2018-09-26
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    相关资源
    最近更新 更多