【问题标题】:Is variadic macro subsitution for every argument possible?是否可以对每个参数进行可变参数宏替换?
【发布时间】:2014-12-15 23:32:57
【问题描述】:

我现在在 SO 上阅读了很多关于可变参数宏的问题,但似乎没有人回答最简单的问题:

#define IDENTITY(x) x
#define IDENTITY_FOR_ALL(...) ???

有没有办法让所有参数的IDENTITY_FOR_ALL 扩展为IDENTITY(X)?是否也可以使用任意数量的参数?

【问题讨论】:

  • 你能提供一个输入样本以及它应该被预处理成什么吗?
  • 您确定不能使用可变参数模板代替宏吗?
  • @chris IDENTITY 是输出。我只希望它被预处理到另一个宏,没有别的。
  • @Deduplicator 是的。在我的用例中,带有一个参数的宏将用于定义枚举的值。我必须做预处理器文本替换工作。
  • 是否要将IDENTITY_FOR_ALL(x,y,z,w) 扩展为x,y,z,w

标签: c++ c++11 c-preprocessor variadic-macros


【解决方案1】:

假设你需要一个PP解决方案,你可以使用BOOST_PP_REPEAT

//invoke IDENTITY_FOR_ALL_MACRO with each index and the given tuple
#define IDENTITY_FOR_ALL(...)                   \
    BOOST_PP_REPEAT(                            \
        BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),    \
        IDENTITY_FOR_ALL_MACRO,                 \
        BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
    )

//use the index to access the right element of the passed tuple
#define IDENTITY_FOR_ALL_MACRO(z, n, data) \
    IDENTITY(BOOST_PP_TUPLE_ELEM(n, data))

IDENTITY_FOR_ALL(abc, 123, "woopee")
//translated to abc 123 "woopee"

如果您需要使用多个不同的宏而不只是 IDENTITY 来执行此操作,那么将其转换为一个更通用的宏会非常简单,该宏需要调用一个宏并一个一个传递单个参数的列表。

我不是 100% 确定任意数量的参数是什么意思,但是如果您想一次使用两个参数而不是一个参数调用 IDENTITY,您可以将底部宏更改为使用 BOOST_PP_MULBOOST_PP_INC 访问元组的第“2n”个和“2n+1”个元素,然后在REPEAT 调用中调用宏的次数只有一半。

【讨论】:

  • +1 #include <boost/preprocessor.hpp> boost.org/doc/libs/1_43_0/libs/preprocessor/doc/index.html
  • 我的意思是任意数量的参数。我已经看到了一些用于可变参数宏解包的专门解决方案,这些解决方案涉及为每个参数数量定义宏,所以我想知道是否有更好的解决方案。显然有。
  • @iFreilicht,啊,是的,Boost 已经为你做到了。单独的REPEAT 类似于 3*256 单独的宏加上选择要调用的正确宏的宏。
【解决方案2】:

没有像可变参数模板那样的可变参数宏的包扩展。

不过,您可以使用 Boost.Preprocessor(或其方法)。

如果您不想在元素之间使用逗号,请使用

#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define ID_OP(_, func, elem) func(elem)
#define APPLY_TO_ALL(func, ...)                \
    BOOST_PP_SEQ_FOR_EACH(                     \
        ID_OP, func,                           \
        BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)  \
    )

// example call:

#define SomeTransformation(x) #x // stringize the argument

APPLY_TO_ALL(SomeTransformation, 1, 2, 3) // expands to "1" "2" "3"

Demo。 用逗号:

#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define ID_OP(_, func, elem) func(elem)
#define APPLY_TO_ALL(func, ...)               \
    BOOST_PP_SEQ_ENUM(                        \
    BOOST_PP_SEQ_TRANSFORM(                   \
        ID_OP, func,                          \
        BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) \
    ))

// example call:

APPLY_TO_ALL(SomeTransformation, 1, 2, 3) // expands to "1", "2", "3"

Demo。 使用g++ -std=c++11 -E -P file 检查预处理器输出。

【讨论】:

  • 当我需要访问每个元素时,我需要学习 SEQ。 TUPLE 没有 FOR_EACH
  • @chris 是的,SEQ 真的很性感(没有双关语)。
  • 似乎第一个(这是我想要的)在 VC2013 下不起作用。 identifier "BOOST_PP_IIF_0" is undefined :(
  • @iFreilicht 你确定你正确地包含了所有的标题吗?编辑:事后看来,我认为这不是问题。
  • @Loopunroller 发现问题。 BoostPP 错误地定义了它的可变参数宏。在config.hpp 中,它不仅检查_MSC_VER,还检查__EDG__(不管是什么)。如果定义了后者,则不会定义BOOST_PP_VARIADICSBOOST_PP_VARIADICS_MSVC。在包含标题之前这样做之后,一切都按预期工作。
猜你喜欢
  • 1970-01-01
  • 2010-12-24
  • 1970-01-01
  • 2013-06-27
  • 2021-12-26
  • 1970-01-01
  • 2019-10-20
  • 1970-01-01
相关资源
最近更新 更多