【问题标题】:How do I generate an arbitrarily long list of parameters using macros in C++?如何使用 C++ 中的宏生成任意长的参数列表?
【发布时间】:2014-07-11 01:36:05
【问题描述】:

我想要一个宏,它接受任意偶数个参数,将每一对拼接在一起,在末尾添加一些名称,比如说Type,然后将它们作为模板参数传递给某种类型,我们称之为CompileTimeList .所以我会给它这样的东西:

MACRO( \
  First,Set, \
  Second,Type, \
  Third,Thing)

它会扩展为:

CompileTimeList<
  FirstSetType,
  SecondTypeType,
  ThirdThingType>

天真的解决方案是可变参数递归模板,这是不允许的:

Can we have recursive macros?

我可以使用这种方法手动组合一个宏:

Macro recursive expansion to a sequence

但它仅限于我定义的许多参数。我希望能够扩展任意长度的参数。这在 C++11 中可行吗?我也有可用的 Boost 1.55,其他问题中没有提到。

为了尝试实现 chris 的回答,我编写了以下程序:

  1 #include <boost/preprocessor.hpp>
  2
  3 #include <vector>
  4 #include <algorithm>
  5
  6 #define MACRO(...)  \
  7     std::find( \
  8     BOOST_PP_ENUM( \
  9         BOOST_PP_DIV(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 2), \
 10         ENUM_MACRO, \
 11         BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
 12     ) \
 13     )
 14
 15 #define ENUM_MACRO(z, n, data) \
 16     BOOST_PP_CAT( \
 17         BOOST_PP_CAT( \
 18             BOOST_PP_TUPLE_ELEM(BOOST_PP_MUL(n, 2), data), \
 19             BOOST_PP_TUPLE_ELEM(BOOST_PP_INC(BOOST_PP_MUL(n, 2)), data) \
 20         ), \
 21         Type \
 22     )
 23
 24
 25 typedef int FirstNameType;
 26 typedef std::vector<int>::iterator SecondNameType;
 27 typedef std::vector<int>::iterator ThirdNameType;
 28
 29 int main()
 30 {
 31     std::vector<int>();
 32     auto it = MACRO(First, Name, Second, Name, Third, Name);
 33     return 0;
 34 }

这会产生大量与使用逗号有关的错误,开头是:

test.cpp:在函数“int main()”中: src/boost/boost/preprocessor/punctuation/comma.hpp:19:27: 错误: ',' 标记之前的预期主表达式 # 定义 BOOST_PP_COMMA() ,

【问题讨论】:

  • 我会改用MACRO((First, Set), (Second, Type), (Third, Thing)) 并使用BOOST_PP_TUPLE_*
  • @chris 谢谢,我不知道这一点。但是如何解决“不允许递归”的问题呢?
  • @Jason:无论如何都要递归:文件 x.h:#include "x.h"...
  • @KerrekSB 我不确定我是否遵循。你的意思是我应该递归#include
  • @Jason,Boost 提供了用于枚举序列的实用程序。看看boost.org/doc/libs/1_55_0/libs/preprocessor/doc/index.html

标签: c++ macros


【解决方案1】:

您想要的语法可以通过 Boost 使用几个宏来实现:

#define BOOST_PP_VARIADICS
#include <boost/preprocessor.hpp>

//The main part is BOOST_PP_ENUM, which loops the number of arguments / 2 times.
//Each loop, ENUM_MACRO is called with the index and a tuple containing the arguments.
//A comma is inserted between all expansions of ENUM_MACRO.
#define MACRO(...)  \
    CompileTimeList< \
    BOOST_PP_ENUM( \
        BOOST_PP_DIV(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 2), \
        ENUM_MACRO, \
        BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \
    ) \
    >

//Since it's called #args/2 times, we multiply the index by 2.
//All it does is concatenate the first in its pair with the second, and then to Type.
#define ENUM_MACRO(z, n, data) \
    BOOST_PP_CAT( \
        BOOST_PP_CAT( \
            BOOST_PP_TUPLE_ELEM(BOOST_PP_MUL(n, 2), data), \
            BOOST_PP_TUPLE_ELEM(BOOST_PP_INC(BOOST_PP_MUL(n, 2)), data) \
        ), \
        Type \
    )


MACRO(a, b, c, d) //CompileTimeList< abType , cdType >

See it work here.

在当前状态下,此宏将简单地忽略奇数列表中的最后一个参数,使用简单的static_assert 可能会产生错误,或者如果需要,可以对最后一个参数做一些特殊的事情.

【讨论】:

  • 我无法运行它,必须尝试运行它吗?我得到了一堆似乎无关的错误......就像BOOST_PP_COMMA()中的错误@
  • 我已经修改了我的答案,包括我尝试实施您的建议。
  • @Jason 扩展为auto it = std::find( FirstNameType , SecondNameType , ThirdNameType ); 就好了(你可以使用g++ -E 来查看源代码的预处理版本)。你的问题是你不能使用类型作为函数参数。
  • @Jason, BOOST_PP_VARIADICS 需要定义。我不确定是否有一种简单的方法可以将代码更改为不需要它,但是还有一个适当的标头可以包含它(类似于&lt;boost /preprocessor/config/variadics.hpp&gt;。现在由于 Coliru 似乎没有那个标头,我'不确定它是否直接来自&lt;boost/preprocessor.hpp&gt;。无论如何,您的示例does work with Clang,减去作为参数的类型的常规编译错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-11
  • 1970-01-01
  • 2012-07-04
相关资源
最近更新 更多