【问题标题】:Variadic macros: expansion of pasted tokens可变参数宏:粘贴标记的扩展
【发布时间】:2014-12-16 21:20:43
【问题描述】:

我想知道是否可以“嵌套”可变参数宏调用。我只真正关心 GCC 和 Clang。我的宏定义如下所示:

/**
 * @brief Invoke an instance method.
 */
#define $(obj, method, ...) \
    ({ \
        typeof(obj) _obj = obj; \
        _obj->interface->method(_obj, ## __VA_ARGS__); \
    })

我使用它在我的 OO 框架 (https://github.com/jdolan/objectively) 中方便地调用“实例方法”:

$(array, addObject, obj);

工作老板。不幸的是,我还没有找到允许嵌套这些调用的方法,这在某些情况下非常有用;例如:

/**
 * @see MutableSetInterface::addObjectsFromArray(MutableSet *, const Array *)
 */
static void addObjectsFromArray(MutableSet *self, const Array *array) {

    if (array) {
        for (size_t i = 0; i < array->count; i++) {
            $(self, addObject, $(array, objectAtIndex, i));
        }
    }
}

上面的嵌套可变参数宏调用无法编译,因为内部调用从未扩展。是否有可能解决这个问题,或者我已经将预处理器滥用到了极限? :)

【问题讨论】:

  • , ## 技巧不可移植,它是 gcc 特定的。
  • 我知道。它也适用于 Clang。这是我现在需要介绍的仅有的两个编译器。

标签: c gcc macros clang variadic-macros


【解决方案1】:

这是嵌套预处理器宏的常见问题。预处理器扩展规则相当晦涩;相关的 tl;dr 是宏是分层扩展的。解决方法是添加一个可以扩展参数的间接层:

#define MI(obj, method, ...) \
  ({ \
    typeof(obj) _obj = obj; \
    _obj->interface->method(_obj, ## __VA_ARGS__); \
  })

#define M(obj, method, ...) MI(obj, method, __VA_ARGS__)

// This will now expand properly.
M(self, addObject, M(array, objectAtIndex, M(foo, bar, i)))

旁注:注意$ 不是C 的基本源字符集的一部分;使用它可能不便携。

【讨论】:

  • , ## 也不便携。
  • 如果我正确理解了您的答案,那么我只会在有要通过的参数时致电M。如果我曾经尝试在没有超出method 的参数的情况下调用M,则生成的代码将无法解析。换句话说,M(self, methodName) 不会编译。它是否正确?有没有办法让一个“入口点”做正确的事,而不考虑额外的参数?
  • (谢谢,我知道我在这里使用的某些功能是非标准的。就像我提到的,我只关心 GCC 和 Clang。)
  • 我实际上并不认为这是一个错误;我认为这是一个限制。 GCC 手册说“粘贴的令牌”(## 之后的任何内容)都没有展开。因此,如果您想从## 带来的“逗号魔法”中受益,那么您将放弃参数扩展。这是一个无赖。也就是说,您的解决方案肯定比没有好。谢谢!!
  • 可能是。不过,我在那里吃了一惊,我不介意告诉你。当然,不客气。
猜你喜欢
  • 2019-07-21
  • 1970-01-01
  • 1970-01-01
  • 2012-02-29
  • 1970-01-01
  • 2020-03-31
  • 1970-01-01
相关资源
最近更新 更多