C / C ++宏中的逗号

时间:2012-12-12 15:00:23

标签: c++ c macros c-preprocessor

假设我们有像这样的宏

#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

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

7 个答案:

答案 0 :(得分:100)

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

#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"

答案 1 :(得分:94)

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

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

答案 2 :(得分:44)

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

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

答案 3 :(得分:22)

只需将FOO定义为

即可
#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

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

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

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

答案 4 :(得分:3)

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

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

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

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

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

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

答案 5 :(得分:3)

这可以通过P99

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

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

答案 6 :(得分:1)

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