使用宏从__VA_ARGS__生成参数列表

时间:2015-08-06 02:27:04

标签: python c++ c++11 boost macros

只是为了一点背景,这不是一个小小的练习!我正在使用Boost.Python,并且为了避免许多丑陋的样板代码,我使用宏来包装Python包装类中的函数,以便可选地调用方法的Python覆盖(如果存在)。

我把这个难题归结为最简单的形式,here

#include <iostream>

using namespace std;

void foo() { cout << "foo" << endl; }
void bar(char, short, int) { cout <<"bar" << endl; }

#define DEFINE_FUNCTION_WRAPPER(return_type, name, ...)\
return_type name##_wrapper(/* macro expansion */)\
{\
    return name(/* macro expansion */);\
}\

DEFINE_FUNCTION_WRAPPER(void, foo)  // works!
//DEFINE_FUNCTION_WRAPPER(void, foo, char, short, int)  // knowledge insufficient

int main() {
    foo_wrapper();
    //bar_wrapper(0, 1, 2);
}

虽然这显然适用于foo,但我的目标是让DEFINE_FUNCTION_WRAPPER(void, foo, char, short, int)生成一个如下所示的函数包装器:

void bar_wrapper(char _1, short _2, int _3)
{
    return bar(_1, _2, _3);
}

我希望能够指出如何最好地解决这个问题,因为我真的很想掌握这种宏观魔法。

感谢任何帮助!

  

注意:我正在编译MSVC C ++ 11。

1 个答案:

答案 0 :(得分:2)

假设你确实需要宏(我对Boost.Python不太熟悉,但可变参数模板和完美转发与此相同,如果它们适用则更加清晰),你可以使用几个方便的Boost.Preprocessor工具。

问题是宏的末尾的省略号必须至少有一个参数传递到它的位置;它不能为零。为了解决这个问题,你必须在整个过程中放弃至少一个参数名称。我选择让宏根据是否获得任何与参数相关的参数来选择其中一个来填充该省略号。

#include <boost/preprocessor.hpp>

//generate "type _#"
#define PARAMS(z,n,data) BOOST_PP_TUPLE_ELEM(n,data) _##n

//The first variant: with parameters
//parameters: PARAMS(z,0,(char,short,int)), PARAMS(z,1,(char,short,int)), PARAMS(z,2,(char,short,int))
//call: _0, _1, _2

#define DEFINE_FUNCTION_WRAPPER_WITH_PARAMS(return_type, name, ...)\
return_type name##_wrapper(BOOST_PP_ENUM(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), PARAMS, (__VA_ARGS__)))\
{\
    return name(BOOST_PP_ENUM_PARAMS(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), _));\
}

//The second variant: no parameters
#define DEFINE_FUNCTION_WRAPPER_WITHOUT_PARAMS(return_type, name)\
return_type name##_wrapper()\
{\
    return name();\
}

//choose variant based on whether more than two arguments are passed
#define DEFINE_FUNCTION_WRAPPER(...)\
    BOOST_PP_IF(\
        BOOST_PP_GREATER(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 2), \
        DEFINE_FUNCTION_WRAPPER_WITH_PARAMS,\
        DEFINE_FUNCTION_WRAPPER_WITHOUT_PARAMS\
    )(__VA_ARGS__)

//Clang output:
//void foo_wrapper( char _0 , short _1 , int _2){ return foo( _0 , _1 , _2);}
//int bar_wrapper(){ return bar();}

BOOST_PP_ENUM用增加的数字调用给定的宏,我们在PARAMS宏中使用它作为类型元组(传入的数据)和名称中的两个索引。它还在扩展之间添加逗号,但不是在最后一个之后。您可以在代码注释中看到它的扩展。如果需要,可以忽略z

BOOST_PP_ENUM_PARAMS保存单独宏的工作,而是使用“常量”来附加数字。它还在扩展之间加上逗号。我们使用下划线结束_0,_1,_2。