C/C + + 宏中的逗号

假设我们有一个这样的宏

#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

Of course we could do:

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

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

51151 次浏览

如果预处理器支持可变宏:

#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);

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

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

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

其次,你可以在参数前后加上括号:

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

如果你这样做,你可能会发现额外的括号搞乱了结果的语法。

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

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

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);

In C, of course, this isn't necessary because type names can't contain commas outside parentheses. So, for a cross-language macro you can write:

#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

这对 P99来说是可能的:

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

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

如果不能使用括号,并且不喜欢 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"

只要将 FOO定义为

#define UNPACK( ... ) __VA_ARGS__


#define FOO( type, name ) UNPACK type name

然后在类型参数周围使用括号调用它,例如。

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

当然,在对宏定义的评论中举例说明调用是一个好主意。

实际上,只要是最后一个参数,你就可以毫不费力地包含逗号参数,这是通过使用 _ _ VA _ ARGS _ _ 来实现的:

#define EXAMPLE(_arg1_, _arg2_, ...) \
for (int i = _arg1_; i < _arg2_; i++){ __VA_ARGS__ }

since in the context of macros, __VA_ARGS__ simply represents the variadic arguments separated by commas, as in:

EXAMPLE(5, 3, int[2] = {i,i})


//macro sees:   5,   3,   int[2] = {i,   i}

因为 _ _ VA _ ARGS _ _ 在参数之间重新插入了逗号,所以宏将按照预期的方式传递代码。

我应该告诉你不要这样做,但是你完全应该这样做,因为滥用宏是 c + + 最好的部分。