使用宏从 __VA_ARGS__ 生成参数列表
Using macros to generate argument list from __VA_ARGS__
只是为了了解一点背景知识,这不是一个简单的练习!我正在使用 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);
}
我希望找到正确的方向来解决这个问题,因为我真的很想掌握这种宏观魔法。
感谢任何帮助!
NOTE: I'm compiling against MSVC C++11.
假设你真的需要宏(我对 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
保存了单独宏的工作,而是使用 "a constant" 将数字附加到。它还在扩展之间放置逗号。我们使用下划线以 _0、_1、_2 结尾。
只是为了了解一点背景知识,这不是一个简单的练习!我正在使用 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);
}
我希望找到正确的方向来解决这个问题,因为我真的很想掌握这种宏观魔法。
感谢任何帮助!
NOTE: I'm compiling against MSVC C++11.
假设你真的需要宏(我对 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
保存了单独宏的工作,而是使用 "a constant" 将数字附加到。它还在扩展之间放置逗号。我们使用下划线以 _0、_1、_2 结尾。