假设我们有像这样的宏
#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
这不符合人体工程学。必须处理类型不兼容的问题。知道如何用宏来解决这个问题吗?
答案 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)
简单的答案是,你做不到。这是<...>
选择模板参数的副作用; <
和>
也出现在不平衡的上下文中,因此无法扩展宏机制来处理它们,就像处理括号一样。 (一些委员会成员提出了不同的标记,比如说(^...^)
,但他们无法用<...>
来说服大多数问题。)