modernize-use-std-print¶
将对 printf
、fprintf
、absl::PrintF
和 absl::FPrintf
的调用转换为等效的 C++23 的 std::print
或 std::println
调用(根据需要),并相应地修改格式字符串。被替换和替换函数可以通过配置选项自定义。每个作为对 std::string::c_str()
和 std::string::data()
的调用结果的实参将被删除现在不必要的调用,类似于 readability-redundant-string-cstr 检查。
换句话说,它将以下代码行
fprintf(stderr, "The %s is %3d\n", description.c_str(), value);
转换为
std::println(stderr, "The {} is {:3}", description, value);
如果 ReplacementPrintFunction 或 ReplacementPrintlnFunction 选项保留其默认值或设置为其默认值,则此检查仅在使用 -std=c++23 或更高版本时启用。
从 <inttypes.h> 中以 PRI
和 __PRI
开头的宏将被展开,转义将被处理,并且相邻的字符串将被连接起来以形成一个单一的 StringLiteral
,然后格式字符串将被转换。在格式字符串中使用任何其他宏将导致发出警告消息,并且不会执行任何转换。转换后的格式字符串将始终是单个字符串文字。
该检查做得还不错,但并不完美。特别是
它假设格式字符串对于实参是正确的。如果您在使用 -Wformat 编译时收到任何警告,则可能出现错误行为。
在检查运行时,AST 包含一个用于格式字符串的单个
StringLiteral
,其中转义已被展开。检查尝试重建转义序列,它们可能与它们被写入时的序列不同(例如,"\x41\x0a"
将变为"A\n"
,并且"ab" "cd"
将变为"abcd"
。)它支持字段宽度、精度、位置实参、前导零、前导
+
、对齐和备用形式。使用任何不受支持的标志或说明符将导致整个语句保持不变,并且会发出警告。一些不受支持的特定功能包括:
用于千位分隔符的
%'
标志。glibc 扩展
%m
。
printf
和类似函数返回打印的字符数。std::print
不返回。这意味着任何使用返回值的调用将不会被转换。不幸的是,目前这包括显式转换为void
。此检查中的缺陷意味着任何位于GCC
复合语句中的调用都不能被转换,即使结果值没有被使用。
如果转换不完整或不安全,则整个调用将保持不变。
如果调用被认为适合转换,则
printf
、fprintf
、absl::PrintF
、absl::FPrintF
以及由 PrintfLikeFunctions 选项或 FprintfLikeFunctions 指定的任何函数将使用 ReplacementPrintlnFunction 选项指定的函数替换,如果格式字符串以\n
结尾,否则使用 ReplacementPrintFunction。格式字符串将被重写以使用
std::formatter
语言。如果在格式字符串的末尾找到了一个\n
,并且它之前没有r
,那么它将被删除,并且使用 ReplacementPrintlnFunction 而不是 ReplacementPrintFunction。任何对应于
%p
说明符的实参,而std::formatter
无法接受,将被包装在一个static_cast
中,转换为const void *
。任何对应于
%s
说明符的实参,其中实参的类型为signed char
或unsigned char
,将被包装在一个reinterpret_cast<const char *>
中。如果启用了 StrictMode,则任何格式字符串和参数在符号性上不同的实参将被包装在一个适当的
static_cast
中。任何以对
std::string::c_str()
或std::string::data()
的调用结尾的实参将被删除该调用。
选项¶
- StrictMode¶
当为 true 时,检查将在从像
printf
这样的可变参数函数转换时添加强制转换,并将有符号或无符号整型类型(包括来自<cstdint>
、ptrdiff_t
、size_t
和ssize_t
的定长整型类型)打印为相反的符号性,以确保输出与printf
的输出匹配。这在从非可变参数函数转换时不适用,例如absl::PrintF
和fmt::printf
。例如,当 StrictMode 启用时int i = -42; unsigned int u = 0xffffffff; printf("%u %d\n", i, u);
将被转换为
std::print("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));
以确保输出将继续是 -42 的无符号表示和 0xffffffff 的有符号表示(通常分别为 4294967254 和 -1)。当为 false(默认值)时,这些强制转换将不会被添加,这可能会导致输出发生变化。
- PrintfLikeFunctions¶
要替换的(完全限定的)函数名列表(以分号分隔),要求第一个参数包含 printf 样式的格式字符串,并且要格式化的实参紧随其后。支持限定的成员函数名,但替换函数名必须是不限定的。如果既没有设置此选项,也没有设置 FprintfLikeFunctions,那么此选项的默认值为 printf; absl::PrintF,否则为空。
- FprintfLikeFunctions¶
要替换的(完全限定的)函数名列表(以分号分隔),要求第一个参数被保留,第二个参数包含 printf 样式的格式字符串,并且要格式化的实参紧随其后。支持限定的成员函数名,但替换函数名必须是不限定的。如果既没有设置此选项,也没有设置 PrintfLikeFunctions,那么此选项的默认值为 fprintf; absl::FPrintF,否则为空。
- ReplacementPrintFunction¶
在转换期间将用于替换
printf
、fprintf
等的函数,而不是默认的std::print
,当原始格式字符串不以\n
结尾时。预期该函数提供与std::print
兼容的接口。一个合适的候选函数是fmt::print
。
- ReplacementPrintlnFunction¶
在转换期间将用于替换
printf
、fprintf
等的函数,而不是默认的std::println
,当原始格式字符串以\n
结尾时。预期该函数提供与std::println
兼容的接口。一个合适的候选函数是fmt::println
。
- PrintHeader¶
必须包含的头文件,用于 ReplacementPrintFunction 的声明,以便可以添加
#include
指令(如果需要)。如果 ReplacementPrintFunction 为std::print
,那么此选项的默认值为<print>
,否则此选项的默认值为 nothing,并且不会添加#include
指令。