【问题标题】:Using variadic macros or templates to implement a set of functions使用可变参数宏或模板来实现一组函数
【发布时间】:2013-08-14 16:34:22
【问题描述】:

我有一组方法用于实例化和初始化一组对象。 它们看起来都差不多,除了传递给 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 个类似的宏,但是我想知道是否有一种方法可以在不重复代码的情况下获得相同的结果?

【问题讨论】:

  • 可变参数模板的签名应该是template&lt; typename ObjectType, typename... Args &gt; ObjectType* CreateObject(Args... args);。但是你需要一些上下文来扩展参数包。
  • 动态分配、哑指针、两阶段初始化?这是噩梦般的调试会话的秘诀。你应该马上逃跑。

标签: c++ variadic-templates variadic-macros


【解决方案1】:

有几种方法可以解决这个问题。首先,您可以在宏中使用类型化表达式,以便解析类型。所以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; \
}

当然,如果您在 Visual Studio 上使用这些宏,您可能需要一些解决方法。当然,更好的解决方案是编写一个模板化的函数。所以你可以这样称呼你的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;
}

但如果您的编译器不支持可变参数模板,您可以使用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
...

【讨论】:

  • 非常感谢您的详细解答。可悲的是,在我的上下文中,我既不能使用可变参数模板,也不能使用 Boost。您的第一个解决方案在 GCC 下工作正常,但显然 Visual Studio 没有以相同的方式扩展 VA_ARGS,当我使用具有多个参数的解决方案时导致一系列语法错误(参见 @ 987654322@)。我目前正在尝试使用该帖子中提到的解决方法来调整您的解决方案,但到目前为止还没有成功。
  • @user2683028 我更新了答案以显示 Visual Studio 所需的解决方法。
  • 谢谢,我还找到了使用其他帖子的解决方案的解决方法:#define EXPLICIT(...) __VA_ARGS__ 并在 NARGS 和 CAT 周围使用它。
【解决方案2】:

您必须在 Argsargs 之后加上 ... 这里:

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 不支持可变参数模板,但 12 年 11 月的版本支持,请检查您是否拥有最新版本的编译器。

另外,您不需要可变参数宏来重新创建新函数,您可以像这样指定 ObjectType:

ObjectType* obj = CreateObject<ObjectType>(foo, 1, "hi");

【讨论】:

  • 好吧,事实证明我不能使用可变参数模板,因为我们还不支持该功能。感谢您对语法的回答和澄清!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多