119

假设我们有一个这样的宏

#define FOO(type,name) type name

我们可以像这样使用

FOO(int, int_var);

但并不总是那么简单:

FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2

我们当然可以这样做:

 typedef std::map<int, int> map_int_int_t;
 FOO(map_int_int_t, map_var); // OK

这不是很符合人体工程学。必须处理 Plus 类型的不兼容性。知道如何用宏解决这个问题吗?

4

7 回答 7

142

如果您不能使用括号并且您不喜欢 Mike 的 SINGLE_ARG 解决方案,只需定义一个逗号:

#define COMMA ,

FOO(std::map<int COMMA int>, map_var);

如果您想对一些宏参数进行字符串化,这也很有帮助,如

#include <cstdio>
#include <map>
#include <typeinfo>

#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
    " has typeid name \"%s\"", typeid(type).name())

int main()
{
    FOO(std::map<int COMMA int>, std::printf);
}

打印std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE"

于 2013-11-07T16:36:17.343 回答
129

因为尖括号也可以表示(或出现在)比较运算符<、和>,所以宏扩展不能像在括号中那样忽略尖括号内的逗号。(这也是方括号和大括号的问题,即使它们通常作为平衡对出现。)您可以将宏参数括在括号中:<=>=

FOO((std::map<int, int>), map_var);

问题是该参数在宏扩展内保持括号括起来,这会阻止它在大多数情况下被读取为类型。

解决此问题的一个好技巧是,在 C++ 中,您可以使用函数类型从带括号的类型名称中提取类型名称:

template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);

因为形成函数类型会忽略额外的括号,所以您可以在类型名称不包含逗号的情况下使用带或不带括号的宏:

FOO((int), int_var);
FOO(int, int_var2);

当然,在 C 中,这不是必需的,因为类型名称不能包含括号外的逗号。因此,对于跨语言宏,您可以编写:

#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif
于 2012-12-12T15:16:09.817 回答
69

如果您的预处理器支持可变参数宏:

#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name

FOO(SINGLE_ARG(std::map<int, int>), map_var);

否则,它有点乏味:

#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need

FOO(SINGLE_ARG2(std::map<int, int>), map_var);
于 2012-12-12T15:08:00.453 回答
37

只需定义FOO

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

然后总是在类型参数周围加上括号来调用它,例如

FOO( (std::map<int, int>), map_var );

在宏定义的注释中举例说明调用当然是个好主意。

于 2016-03-14T23:06:52.497 回答
4

P99可以做到这一点:

#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()

上面的代码有效地只去除了参数列表中的最后一个逗号。检查clang -E(P99 需要 C99 编译器)。

于 2012-12-12T15:25:30.960 回答
4

至少有两种方法可以做到这一点。首先,您可以定义一个带有多个参数的宏:

#define FOO2(type1, type2, name) type1, type2, name

如果你这样做,你可能会发现你最终定义了更多的宏来处理更多的参数。

其次,您可以在参数周围加上括号:

#define FOO(type, name) type name
F00((std::map<int, int>) map_var;

如果这样做,您可能会发现多余的括号会破坏结果的语法。

于 2012-12-12T15:08:18.043 回答
3

简单的答案是你不能。<...>这是模板参数选择的副作用;<and也出现在不平衡的>上下文中,因此无法扩展宏机制来处理它们,就像处理括号一样。(一些委员会成员曾主张使用不同的标记,例如(^...^),但他们无法说服使用 的大多数问题<...>。)

于 2012-12-12T15:11:33.110 回答