创建可变参数宏的技巧是什么FOO(a1, a2, a3,..., an),它可以扩展到您选择的任何预选有界范围内的FOOn(a1, a2, a3,..., an)值n?也就是说,FOO(a)应该扩展FOO1(a),FOO(a, b, c)对FOO3(a, b, c)等,我知道有一个标准的把戏,但我似乎无法找到它.
请随意将此问题标记为副本,如果答案中还有其他问题,请将其关闭.我怀疑有,但我找不到它.
我们在C,C++和Java的代码库上进行了一些代码清理,修复了有符号/无符号比较,运行静态分析等.
我们得到的警告之一是
warning: ISO C does not permit named variadic macros
Run Code Online (Sandbox Code Playgroud)
和它的同伴警告
warning: ISO C99 requires rest arguments to be used
Run Code Online (Sandbox Code Playgroud)
现在,在C代码中我使用C99标准可变参数宏来解决问题,但在C++代码中,正确的答案是什么?使用相同的C99样式会产生不同的警告
warning: anonymous variadic macros were introduced in C99
Run Code Online (Sandbox Code Playgroud)
我没有看到任何答案.
我们在Linux中使用GCC(G ++)4.4.3.
我希望有一些标志或其他方法可以纠正或禁用它的特定代码部分 - 但它用于几乎每个文件中使用的日志...
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
Run Code Online (Sandbox Code Playgroud)
这是这两个宏的定义; 在后面的代码LOGI,并LOGW使用这种方式
LOGI("accelerometer: x=%f y=%f z=%f",
event.acceleration.x, event.acceleration.y,
event.acceleration.z);
Run Code Online (Sandbox Code Playgroud)
就这样
LOGW("Unable to eglMakeCurrent");
Run Code Online (Sandbox Code Playgroud)
因为我试图避免复杂的宏,#define一般来说,我无法得到这个宏实际意味着什么.这里的3点符号有什么作用?这#define在代码后面有什么变化?
显然我知道3个点用于表示和无限量的参数,但我不知道如何阅读这种情况.
Oracle Solaris Studio 12.3的C++预处理器在扩展时会完全删除空白__VA_ARGS__.
有人可以在他们的系统上确认这种行为吗?它是一个已知的编译器错误吗?这个问题有没有解决方法?
为了说明,这是一个简单的测试程序,vaargs.c:
#include <stdio.h>
#define PRINT(...) printf("%s\n", #__VA_ARGS__)
int main()
{
PRINT(hello world);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
C预处理器按预期工作:
$ cc vaargs.c -o vaargs && ./vaargs
hello world
$ cc -V
cc: Sun C 5.12 SunOS_i386 2011/11/16
Run Code Online (Sandbox Code Playgroud)
但是C++预处理器删除了"hello"和"world"之间的空格:
$ CC vaargs.c -o vaargs && ./vaargs
helloworld
$ CC -V
CC: Sun C++ 5.12 SunOS_i386 2011/11/16
Run Code Online (Sandbox Code Playgroud) 我一直在尝试在C中实现一个函数宏,它将"DEBUG:"添加到参数中,并将其参数传递给printf:
#define DBG(format, ...) printf("DEBUG: " #format "\n", __VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
这给了我gcc中的这个错误:
src/include/debug.h:4:70: error: expected expression before ‘)’ token
#define DBG(format, ...) printf("DEBUG: " #format "\n", __VA_ARGS__)
^
Run Code Online (Sandbox Code Playgroud)
据说,它应该串行化格式,并将其变量参数传递给printf,但到目前为止,我无法通过此错误.
编辑
在放弃了字符串化参数和double-hashing(##)后,__VA_ARGS__我现在有了这个错误:
src/lib/cmdlineutils.c: In function ‘version’:
src/lib/cmdlineutils.c:56:17: warning: ISO C99 requires rest arguments to be used [enabled by default]
DBG("version()");
Run Code Online (Sandbox Code Playgroud)
我应该在争论后放置逗号吗?
DBG("version()",); // ?
Run Code Online (Sandbox Code Playgroud)
作为参考,DBG()现在看起来像这样:
#define DBG(format, ...) printf("DEBUG: " format "\n", ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud) 我想知道##这个宏定义有什么作用:
#define debug(M, ...) fprintf(stderr,M "\n",##__VA_ARGS __)
Run Code Online (Sandbox Code Playgroud)
我用谷歌搜索了一个答案,我想出了以下内容。
##如果没有给宏提供变量参数,将删除逗号。所以,如果宏是这样调用的
debug("message");
Run Code Online (Sandbox Code Playgroud)
没有引号,它被扩展为
fprintf(stderr,"message");
Run Code Online (Sandbox Code Playgroud)
不是
fprintf(stderr,"message",);
Run Code Online (Sandbox Code Playgroud)
为什么要去掉逗号?
我一直在寻找一种方法来检查可变参数宏参数列表是否为空。我发现所有解决方案似乎都非常复杂,或者使用了非标准扩展。
我想我找到了既紧凑又标准的简单解决方案:
#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
Run Code Online (Sandbox Code Playgroud)
问:在任何情况下我的解决方案都会失败或调用定义不明确的行为吗?
基于C17 6.10.3.2/2(#运算符):“对应于一个空参数的字符串文字是""”,我相信它#__VA_ARGS__总是定义明确的。
宏说明:
"",它仅包含一个空终止符,因此大小为1。考虑这个宏:
#define MAKE_TEMPLATE(...) template <typename T, __VA_ARGS__ >
Run Code Online (Sandbox Code Playgroud)
当使用零参数时,它会产生错误代码,因为编译器需要逗号后面的标识符.实际上,VC的预处理器足够聪明,可以删除逗号,但GCC不是.由于宏不能重载,看起来这个特殊情况需要一个单独的宏才能正确,如:
#define MAKE_TEMPLATE_Z() template <typename T>
Run Code Online (Sandbox Code Playgroud)
有没有办法让它工作而不引入第二个宏?
调用M其可变参数的参数没有参数的可变参数宏是否合法?
相关标准报价为[cpp.replace]/4:
如果宏定义中的标识符列表不以省略号结尾,则在调用类函数宏时,参数的数量(包括那些不包含预处理标记的参数)应等于宏定义中的参数数量.否则,调用中的参数应该多于宏定义中的参数(不包括
...).应该存在一个)终止调用的预处理标记.
对于没有非可变参数的情况,表单中的调用M()应该是合法的,因为调用有一个参数(不包含预处理标记).因此,除了非变量参数之外还有一个参数.
对于具有一个非可变参数的情况,是否应该有一个尾随,,M(1,)以引入一个由variadic参数组成的参数?否则,参数的数量将等于非可变参数的数量.即
#define variadic(x,...) #__VA_ARGS__
variadic(1,) // 2 arguments: ok
variadic(1) // 1 argument: wrong?
Run Code Online (Sandbox Code Playgroud)
但是,Clang和GCC都接受以下测试用例:
#include <iostream>
#define variadic(x,...) #__VA_ARGS__
int main()
{
std::cout << "'" variadic(1) "'" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
并作为输出产生:
''
Run Code Online (Sandbox Code Playgroud)
这是非标准行为吗?
我试图在C中进行面向对象,并希望为符号设置一个语法糖宏
object->vtable->method(object, arg1, arg2)
Run Code Online (Sandbox Code Playgroud)
成
send(object, method, arg1, arg2)
Run Code Online (Sandbox Code Playgroud)
不幸的是,当一个方法没有参数时,会出现尾随的逗号问题
send(object, method)
Run Code Online (Sandbox Code Playgroud)
给
object->vtable->method(object, )
Run Code Online (Sandbox Code Playgroud)
是否有任何便携式(无##__VA_ARGS__或Visual Studio)方式?
我想出了一个,但我需要交换对象和方法
#define FIRST_ARG_(N, ...) N
#define FIRST_ARG(args) FIRST_ARG_(args)
#define send(msg, ...) \
FIRST_ARG(__VA_ARGS__)->vtable->msg(__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
许可证
send(method, object)
send(method, object, arg1, arg2)
Run Code Online (Sandbox Code Playgroud)
编辑
在下面两个很好的答案的帮助下,我将使用这些宏来完成.它最多可以处理16个参数,但可以轻松扩展
#define SEND_NO_ARG(obj, msg) obj->vtable->msg(obj)
#define SEND_ARG(obj, msg, ...) obj->vtable->msg(obj, __VA_ARGS__)
#define GET_18TH_ARG(arg1, arg2, arg3, arg4, arg5, \
arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, \
arg16, arg17, arg18, ...) arg18
#define SEND_MACRO_CHOOSER(...) \
GET_18TH_ARG(__VA_ARGS__, …Run Code Online (Sandbox Code Playgroud)