对于任何中型到大型项目来说,用自定义日志函数替换 printf
都是很常见的。这是一个最小的 C++ 示例及其用法:
#include <stdio.h>
#include <stdarg.h>
#include <string>
void log_printf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap); // real code obviously does something fancier
va_end(ap);
}
int main() {
std::string x = "Hello";
// correct code
printf("String is %s\n", x.c_str());
log_printf("String is %s\n", x.c_str());
// incorrect code
printf("String is %s\n", x); // bad line 1
log_printf("String is %s\n", x); // bad line 2
}
简单记录器接收可变数量的参数并调用 vprintf 将它们输出到标准输出。 “正确代码”下的行演示了此记录器的正确用法。我的问题涉及“坏”行,其中错误地传递了字符串对象而不是指向字符缓冲区的指针。
在 GCC 4.6 下(在 Linux 下测试),两个坏行都无法编译,这是一件好事,因为我想捕获这种不正确的用法。错误是:
error: cannot pass objects of non-trivially-copyable type ‘std::string {aka struct std::basic_string<char>}’ through ‘...’
然而,在 GCC 5.1 中,显然可以传递非平凡可复制的对象,并且编译成功。如果我使用 -Wall,则只有“bad line 1”会引发有关意外参数类型的警告,但带有 log_printf 的“bad line 2”在任何情况下都可以编译而不会出现问题。不用说,两条线都会产生垃圾输出。
我可以使用 -Wall -Werror
捕获“坏线 1”,但是“坏线 2”呢?我怎样才能让它也产生编译错误?
最佳答案
对于您自己的功能,您需要使用 common function attribute调用格式
:
void log_printf(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
void log_printf(const char* fmt, ...) {
...
}
请注意,该属性必须在函数声明上设置,而不是在定义上设置。
format
属性的第一个参数是样式,在本例中是 printf
(scanf
也是可能的,对于像这样工作的函数scanf
),第二个参数是格式字符串,第三个参数是省略号 ...
所在的位置。它将帮助 GCC 检查格式字符串,就像标准 printf
函数一样。
这是一个 GCC 扩展,尽管其他一些编译器已采用它来兼容 GCC,最著名的是 Intel C 编译器 ICC 和 Clang(OSX 和某些 BSD 变体上使用的标准编译器)。 Visual Studio 编译器没有这个扩展,据我所知,Visual C++ 也没有类似的东西。
关于c++ - 错误地将字符串传递给 printf 样式日志函数时丢失错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36407973/