假设有一个调试功能,简化如下:
void DumpString(char* var, char* varname) {
printf("%s : '%s'\n", varname, var);
}
char str[10]="foobar";
DumpString(str, "str");
> str : foobar
让我们更容易删除两次传递变量的不必要的无关要求,一次用引号:
#define VARASSTR(v) #v
void DumpString(char* var) {
printf("%s : '%s'\n", VARASSTR(var), var);
}
char str[10]="foobar";
DumpString(str);
> var : foobar
糟糕!它使用局部变量名而不是传入的名称。让我们尝试不同的(并且不太理想)大头钉:
#define DumpStr(v) DumpString(v, #v)
void DumpString(char* var, char* varname) {
printf("%s : '%s'\n", varname, var);
}
char str[10]="foobar";
DumpStr(str);
> str : foobar
很棒。但如果功能稍微复杂一点怎么办:
void DumpString(char* var, char* varname, int optionalvar=0) {
printf("%s : '%s'\n", varname, var);
printf("blah: %d", optionalvar);
}
无法重载宏,因此DumpStr
无效,我们已经排除了VARASSTR
的版本。
如何处理(不使用多个类似但功能不同的函数/宏)?
答案 0 :(得分:1)
这是非标准的,但在GNU C中作为扩展:
#define DumpStr(v, ...) DumpString(v, #v, ##__VA_ARGS__)
在GNU C中,您可以不向可变参数宏传递任何参数,并且在逗号和空可变参数列表之间应用时,“令牌粘贴运算符”##
不会产生任何内容(因此尾随逗号被抑制)
在Visual C ++中,我认为令牌粘贴操作符##
是不必要的(并且可能会破坏宏),因为如果它出现在空的可变参数列表之前,Visual C ++会自动抑制尾随逗号。
请注意,唯一使这种非标准的是希望有时传递一个空参数列表。 Variadic宏在C99和C ++ 11中都是标准化的。
编辑这是一个不使用非标准功能的示例。你可以看到为什么有些人真的希望在标准中解决这类问题:
#define DUMPSTR_1(v) DumpString(v, #v)
#define DUMPSTR_2(v, opt) DumpString(v, #v, opt)
#define DUMPSTR_NARG(...) DUMPSTR_ARG_N(__VA_ARGS__, 4, 3, 2, 1, 0)
#define DUMPSTR_ARG_N(_1, _2, _3, _4, n, ...) n
#define DUMPSTR_NC(f, ...) f(__VA_ARGS__)
#define DUMPSTR_NB(nargs, ...) DUMPSTR_NC(DUMPSTR_ ## nargs, __VA_ARGS__)
#define DUMPSTR_NA(nargs, ...) DUMPSTR_NB(nargs, __VA_ARGS__)
#define DumpStr(...) DUMPSTR_NA(DUMPSTR_NARG(__VA_ARGS__), __VA_ARGS__)
可能有一些更简洁的方法可以做到这一点。但不是那么多。
编辑2:这是另一个不使用非标准功能的示例,R..提供
#define STRINGIFY_IMPL(s) #s
#define STRINGIFY(s) STRINGIFY_IMPL(s)
#define ARG1_IMPL(a, ...) a
#define ARG1(...) ARG1_IMPL(__VA_ARGS__, 0)
#define DumpStr(...) DumpString(STRINGIFY(ARG1(__VA_ARGS__)), __VA_ARGS__)
请注意,这需要更改DumpString
的参数顺序,以便字符串化的函数名称是第一个参数。