使用可变宏或模板实现一组函数 [英] Using variadic macros or templates to implement a set of functions

查看:146
本文介绍了使用可变宏或模板实现一组函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一套方法用来建构和初始化一组对象。
除了传递给 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)\ 
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,〜)
pre>

编辑:Heres是您需要获得上述宏在msvc中工作的解决方法:

  / *这计算args的数目* / 
#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
...
/ pre>

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆