【问题标题】:VC++ vs GCC preprocessorVC++ 与 GCC 预处理器
【发布时间】:2014-09-13 17:02:10
【问题描述】:

长话短说,gcc 和 vc++ 预处理器具有不同的输出和相同的输入。如果传递给另一个宏,似乎 vc++ 中的可变参数宏不会进行“参数匹配”(如果它是正确的术语)。 例如:

#define MACRO(a, ...)        head:a, tail:MACRO_OTHER(__VA_ARGS__)
#define MACRO_OTHER(a, ...)  head:a, tail:__VA_ARGS__

MACRO(1, 2, 3, 4, 5)

gcc 输出:

head:1, tail:head:2, tail:3,4,5

vc++ 输出:

head:1, tail:head:2,3,4,5, tail:

显然MACRO_OTHER 中的a2,3,4,5,带有空的可变参数部分。 考虑到这一点,有没有办法创建一个 vc++ 替代以下宏(与 gcc 配合得很好)

#define VA_TYPES_WITH_ARGS(...) __VA_TYPES_WITH_ARGS(VA_NUM_ARGS(__VA_ARGS__),##__VA_ARGS__)
#define __VA_TYPES_WITH_ARGS(n, ...) _VA_TYPES_WITH_ARGS(n,##__VA_ARGS__)
#define _VA_TYPES_WITH_ARGS(n, ...) _VA_TYPES_WITH_ARGS_##n(__VA_ARGS__)
#define _VA_TYPES_WITH_ARGS_0()
#define _VA_TYPES_WITH_ARGS_1(type     ) type _arg1
#define _VA_TYPES_WITH_ARGS_2(type, ...) type _arg2, _VA_TYPES_WITH_ARGS_1(__VA_ARGS__)
#define _VA_TYPES_WITH_ARGS_3(type, ...) type _arg3, _VA_TYPES_WITH_ARGS_2(__VA_ARGS__)
// etc

它基本上为每个参数附加_argK

例子:

VA_TYPES_WITH_ARGS(int, bool, float)

将扩展为

int _arg3, bool _arg2, float _arg1

任何帮助将不胜感激。


相关的预处理器问题:

Difference between gcc and Microsoft preprocessor

Unexpected Behaviour of GCC and VC++ Preprocessor

【问题讨论】:

  • VC++ 在涉及到 C99 时是高度瘫痪的。您可以在 Boost.Preprocessor 中到处看到解决方法。无论如何,如果我没记错的话,你可以使用 Boost 的 BOOST_PP_ENUM 宏来做你想做的事情。
  • @chris 但是 VC++ 确实针对 C++11,它提供了与 C99 wrt 可变参数宏和空宏参数相同的功能。至少,如果某些有效的 C++11 不能在 VC++ 中工作,那么 VC++ 很有可能会被修复以使其工作。确实,C99 并非如此。 :)
  • @hvd,目标和实现是完全不同的事情。看看所有出现的可变参数模板问题。无论如何,C99 和 C++11 之间的预处理器是相同的,只是减去了一些较小的东西,例如 ::,不是吗?
  • @chris 哦,当然,我没有对此提出异议,并修改了我之前的评论以更清楚地说明我的意思。
  • 顺便说一句,你使用的是reserved identifiers

标签: c++ gcc visual-c++ macros c-preprocessor


【解决方案1】:

您可以通过使用 Boost 以合理的交叉编译器方式执行此操作(并接受更多参数而无需启动额外工作),我可以告诉您,花费这么多时间查看标头,有很多解决方法解决诸如此类的问题:

#define VA_TYPES_WITH_ARGS(...)   \
    BOOST_PP_ENUM(                \
        BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),    \
        VA_TYPES_WITH_ARGS_MACRO,               \
        BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
    )

#define VA_TYPES_WITH_ARGS_MACRO(z, n, data)       \
    BOOST_PP_TUPLE_ELEM(n, data) BOOST_PP_CAT(     \
        _arg,                                      \
        BOOST_PP_SUB(BOOST_PP_TUPLE_SIZE(data), n) \
    )                                              

VA_TYPES_WITH_ARGS(int, bool, float) //int _arg3 , bool _arg2 , float _arg1

第一个宏在可变参数上枚举 (ENUM),为每个宏调用第二个并添加连接逗号。它从可变参数数据形成一个元组以提供给第二个宏。

第二种形式的元素 (TUPLE_ELEM) 后跟 _arg 连接 (CAT) 和 size - n (SUB),其中 size 是可变参数数据中的元素数量(给定元组形式的宏) (TUPLE_SIZE)。

See it work.

【讨论】:

  • 谢谢,这正是我想要的。但我忘了提到,如果没有提供任何参数,它应该扩展为空。此实现扩展为 _arg1
  • @vim,我真的想不出比在第一个宏中使用一些ISEMPTY macro 作为BOOST_PP_IF 的条件更简单的方法了,没有任何东西作为真正的分支,而这作为假分支。
  • 我试过了,但问题是没有参数的宏和有一个参数的宏的参数大小都是 1,所以很难说在这种情况下我们应该测试什么。跨度>
  • @vim,你会测试ISEMPTY(__VA_ARGS__)。当不带参数调用时,宏会被赋予一个 empty 参数。
  • @vim,是的,我的建议是错误的。我忘记了,只要将现有宏放入BOOST_PP_IF,它就会放入额外的逗号,这意味着BOOST_PP_IF 的额外参数。我已经 amended the example 使用可变参数数据调用现有宏或调用 BOOST_PP_TUPLE_EAT() 的结果,它只是忽略参数并扩展为空。
【解决方案2】:

适用于 VC++ 并与 GCC 不同的独立方法是:

#define EXPAND(...) __VA_ARGS__
#define LPAREN (
#define RPAREN )

现在,不要使用像 x(__VA_ARGS__) 这样的宏,而是像这样使用它:EXPAND(x LPAREN __VA_ARGS__ RPAREN)

这会强制 VC++ 预处理器比正常情况下扫描参数的时间晚。

您应该能够将这两种形式结合起来,将编译器特定的位放在一个地方。

请注意,虽然不能有一个标准的 C++ 答案:您依赖 x() 是一个没有参数的宏调用。这不是预处理器在标准 C++ 中的工作方式:如果 x 是可变参数宏,x() 使用单个参数调用宏,并且该单个参数为空。此外,您依赖于能够省略可变参数:标准 C++ 也不允许这样做。如果宏定义为#define FOO(x,...),则以FOO(1) 调用它是无效的。由于没有标准的方法,希望各种方法必然是特定于编译器的,这对您来说不是问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 2011-04-24
    • 2014-06-14
    • 2021-11-27
    • 2021-03-19
    • 2017-01-15
    相关资源
    最近更新 更多