使用可变宏或模板实现一组函数 [英] Using variadic macros or templates to implement a set of functions
问题描述
我有一套方法用来建构和初始化一组对象。
除了传递给 Init 函数的参数数量,它们都看起来几乎一样:
ObjectType * CreateObjectType(Arg1 a1,Arg2 arg2,... ArgN aN)
{
ObjectType * object = new ObjectType();
[...]
object-> Init(this,a1,a2,...,aN);
[...]
}
请注意,参数不会在任何地方使用,除非传递给 Init 函数。
我想找到一种方法来实现所有这些,而不必为每个对象类型复制代码。
我尝试使用可变参数宏,具有以下(无效)结果:
#define CREATE_OBJECT_IMPL(ObjectType,...)\
ObjectType * Create ## ObjectType ##(__ VA_ARGS__)\
{\
ObjectType * object = new ObjectType(); \
[...]
object-> Init(this,## __ VA_ARGS__); \
[...]
return object; \
}
//这是我想要实现的结果:
CREATE_OBJECT_IMPL(MyFirstObject,bool,float)
CREATE_OBJECT_IMPL(MySecondObject,int)
CREATE_OBJECT_IMPL(MyThirdObject)
现在,在这个实现中,我使用了VA_ARGS两次, :
-
在第一种情况下,我想要一个具有指定类型的参数列表(Arg1 a1,Arg2 a2 ...)
-
在第二种情况下,我想通过它们的名称(Init(a1,a2 ...))调用这些参数。
我尝试使用可变参数模板:
模板< typename ObjectType,typename ... Args>
ObjectType * CreateObject(Args args)
{
ObjectType * object = new ObjectType();
[...]
object-> Init(this,args);
[...]
}
#define CREATE_OBJECT_IMPL(ObjectType,...)\
ObjectType * Create ## ObjectType ##(__ VA_ARGS__)\
{\
return CreateObject< ObjectType,__VA_ARGS __>(__ VA_ARGS__); \
}
...但这似乎并不奏效,我在模板定义行上得到以下错误:
错误C2143:语法错误:缺少','before'...' / p>
错误C2065:'Args':未指定的标识符
。
我仍然可以为每个参数编写N个相似的宏,但是我想知道是否有一种方法可以获得相同的结果而不重复代码?
有几种方法可以解决这个问题。首先,您可以在宏中使用类型表达式,以便您可以解析类型。因此, CREATE_OBJECT_IMPL
将被这样调用:
CREATE_OBJECT_IMPL (Arg1)arg1,(Arg2)arg2)
这里有一些宏,关闭类型:
#define EAT(x)
#define REM(x)x
# define STRIP(x)EAT x
#define PAIR(x)REM x
工作这样。当您写 STRIP((Arg1)arg1)
时,它将扩展为 arg1
。当你写 PAIR((Arg1)arg1)
时,它将扩展为 Arg1 arg1
。接下来,你要做的是将这些宏应用到每个传递的参数,所以这里是一个简单的 APPLY
宏,它将允许你做8参数:
/ *计算args的数目* /
#define NARGS_SEQ(_1,_2,_3 ,_4,_5,_6,_7,_8,N,...)N
#define NARGS(...)NARGS_SEQ(__ VA_ARGS__,8,7,6,5,4,3,2,1)
/ *这将让宏在合并之前扩展* /
#define PRIMITIVE_CAT(x,y)x ## y
#define CAT(x,y)PRIMITIVE_CAT x,y)
/ *这将调用在* /
#define中传递的每个参数的宏。APPLY(macro,...)CAT(APPLY_,NARGS(__ VA_ARGS __))宏,__VA_ARGS__)
#define APPLY_1(m,x1)m(x1)
#define APPLY_2(m,x1,x2)m(x1),m(x2)
#define APPLY_3 (m,x1,x2,x3,x4)m(x1),m(x2),m(x1) ,m(x2),m(x3),m(x4),m(x3),m(x3),m(x4),
#define APPLY_5(m,x1,x2,x3,x4,x5) m(x5)
#define APPLY_6(m,x1,x2,x3,x4,x5,x6)m(x1),m(x2) ,m(x2),m(x3),m(x4),m(x4),m(x4),m (x5),m(x6),m(x7)
#define APPLY_8(m,x1,x2,x3,x4,x5,x6,x7,x8) (x3),m(x4),m(x5),m(x6),m(x7),m(x8)
然后你可以这样定义 CREATE_OBJECT_IMPL
:
#define CREATE_OBJECT_IMPL(ObjectType,...)\
ObjectType * Create ## ObjectType(APPLY(PAIR,__VA_ARGS__))\
{\
ObjectType * object = new ObjectType(); \
[...] \
object-> Init(this,APPLY(STRIP,__VA_ARGS__)); \
[...] \
返回对象; \
}
当然,您可能需要这些宏的一些解决方法,使用他们在视觉工作室。当然,一个更好的解决方案是写一个模板化的函数。所以你可以这样调用 CreateObject
:
ObjectType * obj = CreateObject< ObjectType>(arg1,arg2,arg3);
在C ++ 11中,您可以使用如下的varidiac模板:
template< typename ObjectType,typename ... Args>
ObjectType * CreateObject(Args ... args)
{
ObjectType * object = new ObjectType();
[...]
object-> Init(this,args ...);
[...]
}
但是如果你的编译器不支持varidiac模板, a href =http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/index.html> Boost.PP 可为最多10个参数生成重载(或更多if您需要):
#define GENERATE_OBJS_EACH(z,n,data)\
pre>
template< class ObjectType, BOOST_PP_ENUM_PARAMS_Z(z,n,class Arg)> \
ObjectType * CreateObject(BOOST_PP_ENUM_BINARY_PARAMS_Z(z,n,Arg,arg))\
{\
ObjectType * object = new ObjectType(); \
[...] \
object-> Init(this,BOOST_PP_ENUM_PARAMS_Z(z,n,arg)); \
[...] \
返回对象; \
}
/ *为最多10个参数生成CreateObject模板* /
BOOST_PP_REPEAT_FROM_TO_1(1,10,GENERATE_OBJS_EACH,〜)
编辑:Heres是您需要获得上述宏在msvc中工作的解决方法:
/ *这计算args的数目* /
/ pre>
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...)
#define NARGS_MSVC_WORKAROUND(x)NARGS_SEQ x
#define NARGS(...)NARGS_MSVC_WORKAROUND((__ VA_ARGS__,8,7,6,5,4,3,2,1))
/ *这将让宏在连接之前扩展* /
#define PRIMITIVE_CAT(x,y)x ## y
#define CAT_MSVC_WORKAROUND(x)PRIMITIVE_CAT x
#define CAT(x,y)CAT_MSVC_WORKAROUND((x,y))
/ *这将调用每个参数传递的宏* /
#define APPLY APPLY_MSVC_WORKAROUND(CAT(APPLY_,NARGS(__ VA_ARGS__)),(macro,__VA_ARGS__))
#define APPLY_MSVC_WORKAROUND(m,x)mx
...
I have a set of methods used to instanciate and initialize a set of objects. They all look pretty much the same, except for the number of arguments that are passed to the Init function :
ObjectType* CreateObjectType(Arg1 a1, Arg2 arg2, ... ArgN aN) { ObjectType* object = new ObjectType(); [...] object->Init(this, a1, a2, ..., aN); [...] return object; }
Note that the arguments are not to be used anywhere except to be passed to the Init function.
I would like to find a way to implement all of those without having to duplicate the code for each object type.
I tried using variadic macros, with the following (invalid) result :
#define CREATE_OBJECT_IMPL(ObjectType, ...) \ ObjectType* Create##ObjectType##(__VA_ARGS__) \ { \ ObjectType* object = new ObjectType(); \ [...] object->Init(this, ##__VA_ARGS__); \ [...] return object; \ } // This is the result I am trying to achieve : CREATE_OBJECT_IMPL(MyFirstObject, bool, float) CREATE_OBJECT_IMPL(MySecondObject, int) CREATE_OBJECT_IMPL(MyThirdObject)
Now, in this implementation, I used VA_ARGS twice, both times incorrectly :
In the first case, I want to have a list of arguments with the types I specified (Arg1 a1, Arg2 a2...)
In the second case, I want to call these arguments by their names ( Init(a1, a2...) ).
I tried using variadic templates :
template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args);
[...]
return object;
}
#define CREATE_OBJECT_IMPL(ObjectType, ...) \
ObjectType* Create##ObjectType##(__VA_ARGS__) \
{ \
return CreateObject<ObjectType, __VA_ARGS__>(__VA_ARGS__); \
}
...but this doesn't seem to work as well, I get the following error on the template definition line :
error C2143: syntax error : missing ',' before '...'
error C2065: 'Args' : undeclared identifier
I am using VS2012.
I could still write N similar macros for each number of arguments, however I was wondering if there was a way to obtain the same result without duplicating code?
There are a couple of ways to solve the problem. First, you can use typed expression in the macro so you can parse the type. So the CREATE_OBJECT_IMPL
would be called like this:
CREATE_OBJECT_IMPL(Object, (Arg1) arg1, (Arg2) arg2)
Here are some macros that will retrieve the type and strip off the type:
#define EAT(x)
#define REM(x) x
#define STRIP(x) EAT x
#define PAIR(x) REM x
These macros work like this. When you write STRIP((Arg1) arg1)
it will expand to arg1
. And when you write PAIR((Arg1) arg1)
it will expand to Arg1 arg1
. Now next, you will want to do is to apply these macros to each argument that is passed in, so here is a simple APPLY
macro that will let you do that for up to 8 arguments:
/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)
/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) CAT(APPLY_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define APPLY_1(m, x1) m(x1)
#define APPLY_2(m, x1, x2) m(x1), m(x2)
#define APPLY_3(m, x1, x2, x3) m(x1), m(x2), m(x3)
#define APPLY_4(m, x1, x2, x3, x4) m(x1), m(x2), m(x3), m(x4)
#define APPLY_5(m, x1, x2, x3, x4, x5) m(x1), m(x2), m(x3), m(x4), m(x5)
#define APPLY_6(m, x1, x2, x3, x4, x5, x6) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define APPLY_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define APPLY_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)
Then you can define the CREATE_OBJECT_IMPL
like this:
#define CREATE_OBJECT_IMPL(ObjectType, ...) \
ObjectType* Create##ObjectType(APPLY(PAIR, __VA_ARGS__)) \
{ \
ObjectType* object = new ObjectType(); \
[...] \
object->Init(this, APPLY(STRIP, __VA_ARGS__)); \
[...] \
return object; \
}
Of course, you might need some workarounds for these macros, if you use them on visual studio. Of course, a better solution is to write a templated function. So you would call your CreateObject
like this:
ObjectType* obj = CreateObject<ObjectType>(arg1, arg2, arg3);
In C++11, you can use varidiac templates like this:
template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args...);
[...]
return object;
}
But if your compiler doesn't support varidiac templates, you can use the Boost.PP to generate overloads for up to 10 arguments(or more if you need to):
#define GENERATE_OBJS_EACH(z, n, data) \
template<class ObjectType, BOOST_PP_ENUM_PARAMS_Z(z, n, class Arg)> \
ObjectType* CreateObject(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, Arg, arg)) \
{ \
ObjectType* object = new ObjectType(); \
[...] \
object->Init(this, BOOST_PP_ENUM_PARAMS_Z(z, n, arg)); \
[...] \
return object; \
}
/* Generate CreateObject template for up to 10 arguments */
BOOST_PP_REPEAT_FROM_TO_1(1, 10, GENERATE_OBJS_EACH, ~)
Edit: Heres are the workarounds you would need to get the above macros to work in msvc:
/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS_MSVC_WORKAROUND(x) NARGS_SEQ x
#define NARGS(...) NARGS_MSVC_WORKAROUND((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1))
/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT_MSVC_WORKAROUND(x) PRIMITIVE_CAT x
#define CAT(x, y) CAT_MSVC_WORKAROUND((x, y))
/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) APPLY_MSVC_WORKAROUND(CAT(APPLY_, NARGS(__VA_ARGS__)), (macro, __VA_ARGS__))
#define APPLY_MSVC_WORKAROUND(m, x) m x
...
这篇关于使用可变宏或模板实现一组函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!