使用可变宏或模板来实现一组函数
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);
[...]
return object;
}
请注意,除了传递给Init函数之外,参数不能在任何地方使用。
我想找到一种方法来实现所有这些,而不必为每个对象类型重复代码。
我尝试使用可变宏,结果如下(无效):
#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)
现在,在这个实现中,我使用了VA_ARGS两次,两次都不正确:
在第一种情况下,我想要一个参数列表,其中包含我指定的类型(Arg1 a1,Arg2 a2…)
在第二种情况下,我想通过它们的名称(Init(a1,a2…))来调用这些参数。
我尝试使用可变模板:
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__);
}
但这似乎不起作用,我在模板定义行上得到以下错误:
错误C2143:语法错误:在"…"之前缺少","
错误C2065:"Args":未声明的标识符
我正在使用VS2012。
我仍然可以为每个数量的参数编写N个类似的宏,但我想知道是否有一种方法可以在不重复代码的情况下获得相同的结果?
有几种方法可以解决这个问题。首先,可以在宏中使用类型化表达式,以便解析类型。因此CREATE_OBJECT_IMPL
将被称为:
CREATE_OBJECT_IMPL(Object, (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个参数执行此操作:
/* 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)
然后你可以这样定义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__));
[...]
return object;
}
当然,如果您在visualstudio上使用这些宏,您可能需要一些变通方法。当然,更好的解决方案是编写一个模板化函数。所以你可以这样称呼你的CreateObject
:
ObjectType* obj = CreateObject<ObjectType>(arg1, arg2, arg3);
在C++11中,你可以使用这样的可变模板:
template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args...);
[...]
return object;
}
但是,如果您的编译器不支持variac模板,您可以使用Boost.PP为多达10个参数生成重载(如果需要,可以生成更多):
#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, ~)
编辑:以下是在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
...
你必须把。。。在Args
和args
之后此处:
ObjectType* CreateObject(Args args)
这里:
object->Init(this, args);
那么代码应该是:
template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args...);
[...]
return object;
}
另一个问题是Visual Studio 2012不支持可变模板,但11月12日发布的版本支持,请检查您是否有最新版本的编译器。
此外,您不需要可变宏来重新创建新函数,您可以这样指定ObjectType:
ObjectType* obj = CreateObject<ObjectType>(foo, 1, "hi");
- 在命名空间内部还是外部实现 c++ 函数?
- 如何在C++中实现函数(f)(x)(y, z)(g)(r)
- 如何在C++中实现函数上的二叉搜索?
- 键按下事件错误 Qt 实现函数时
- 在子类之外实现函数导致未知错误
- 通过模板滥用实现函数式C++
- C++,实现函数"int next(std::string param)"时出现奇怪的编译器错误
- 我正在尝试创建自己的 strcat() 函数,而不是使用库实现函数<cstring>
- 如果未实现函数,则在链接时启用错误
- 用函数参数实现c++函数指针
- 如何在Android SDK中调用和实现C++函数
- 在 C++ RCPP 中实现 R 函数
- 实现函数模板填充多维对象
- 类的层次结构,试图在基本级别实现函数
- 用c++实现函数对象的自动检测类型
- 用shared_ptrs实现函数模板的C++实例化
- 在类中实现函数导致错误:成员引用基类型'ifstream (string)'不是结构或联合
- 用c++实现S函数.生成错误
- 引用可以用于实现函数重写吗
- 如何在c++中实现函数超时