modernize-use-std-print¶
Converts calls to printf
, fprintf
, absl::PrintF
and
absl::FPrintf
to equivalent calls to C++23’s std::print
or
std::println
as appropriate, modifying the format string appropriately.
The replaced and replacement functions can be customised by configuration
options. Each argument that is the result of a call to std::string::c_str()
and
std::string::data()
will have that now-unnecessary call removed in a
similar manner to the readability-redundant-string-cstr check.
In other words, it turns lines like:
fprintf(stderr, "The %s is %3d\n", description.c_str(), value);
into:
std::println(stderr, "The {} is {:3}", description, value);
If the ReplacementPrintFunction or ReplacementPrintlnFunction options are left, or assigned to their default values then this check is only enabled with -std=c++23 or later.
The check doesn’t do a bad job, but it’s not perfect. In particular:
It assumes that the format string is correct for the arguments. If you get any warnings when compiling with -Wformat then misbehaviour is possible.
At the point that the check runs, the AST contains a single
StringLiteral
for the format string and any macro expansion, token pasting, adjacent string literal concatenation and escaping has been handled. Although it’s possible for the check to automatically put the escapes back, they may not be exactly as they were written (e.g."\x0a"
will become"\n"
and"ab" "cd"
will become"abcd"
.) This is helpful since it means that thePRIx
macros from<inttypes.h>
are removed correctly.It supports field widths, precision, positional arguments, leading zeros, leading
+
, alignment and alternative forms.Use of any unsupported flags or specifiers will cause the entire statement to be left alone and a warning to be emitted. Particular unsupported features are:
The
%'
flag for thousands separators.The glibc extension
%m
.
printf
and similar functions return the number of characters printed.std::print
does not. This means that any invocations that use the return value will not be converted. Unfortunately this currently includes explicitly-casting tovoid
. Deficiencies in this check mean that any invocations insideGCC
compound statements cannot be converted even if the resulting value is not used.
If conversion would be incomplete or unsafe then the entire invocation will be left unchanged.
If the call is deemed suitable for conversion then:
printf
,fprintf
,absl::PrintF
,absl::FPrintF
and any functions specified by the PrintfLikeFunctions option or FprintfLikeFunctions are replaced with the function specified by the ReplacementPrintlnFunction option if the format string ends with\n
or ReplacementPrintFunction otherwise.the format string is rewritten to use the
std::formatter
language. If a\n
is found at the end of the format string not preceded byr
then it is removed and ReplacementPrintlnFunction is used rather than ReplacementPrintFunction.any arguments that corresponded to
%p
specifiers thatstd::formatter
wouldn’t accept are wrapped in astatic_cast
toconst void *
.any arguments that corresponded to
%s
specifiers where the argument is ofsigned char
orunsigned char
type are wrapped in areinterpret_cast<const char *>
.any arguments where the format string and the parameter differ in signedness will be wrapped in an appropriate
static_cast
if StrictMode is enabled.any arguments that end in a call to
std::string::c_str()
orstd::string::data()
will have that call removed.
Options¶
- StrictMode¶
When true, the check will add casts when converting from variadic functions like
printf
and printing signed or unsigned integer types (including fixed-width integer types from<cstdint>
,ptrdiff_t
,size_t
andssize_t
) as the opposite signedness to ensure that the output matches that ofprintf
. This does not apply when converting from non-variadic functions such asabsl::PrintF
andfmt::printf
. For example, with StrictMode enabled:int i = -42; unsigned int u = 0xffffffff; printf("%d %u\n", i, u);
would be converted to:
std::print("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));
to ensure that the output will continue to be the unsigned representation of -42 and the signed representation of 0xffffffff (often 4294967254 and -1 respectively.) When false (which is the default), these casts will not be added which may cause a change in the output.
- PrintfLikeFunctions¶
A semicolon-separated list of (fully qualified) function names to replace, with the requirement that the first parameter contains the printf-style format string and the arguments to be formatted follow immediately afterwards. If neither this option nor FprintfLikeFunctions are set then the default value for this option is printf; absl::PrintF, otherwise it is empty.
- FprintfLikeFunctions¶
A semicolon-separated list of (fully qualified) function names to replace, with the requirement that the first parameter is retained, the second parameter contains the printf-style format string and the arguments to be formatted follow immediately afterwards. If neither this option nor PrintfLikeFunctions are set then the default value for this option is fprintf; absl::FPrintF, otherwise it is empty.
- ReplacementPrintFunction¶
The function that will be used to replace
printf
,fprintf
etc. during conversion rather than the defaultstd::print
when the originalformat string does not end with\n
. It is expected that the function provides an interface that is compatible withstd::print
. A suitable candidate would befmt::print
.
- ReplacementPrintlnFunction¶
The function that will be used to replace
printf
,fprintf
etc. during conversion rather than the defaultstd::println
when the original format string ends with\n
. It is expected that the function provides an interface that is compatible withstd::println
. A suitable candidate would befmt::println
.
- PrintHeader¶
The header that must be included for the declaration of ReplacementPrintFunction so that a
#include
directive can be added if required. If ReplacementPrintFunction isstd::print
then this option will default to<print>
, otherwise this option will default to nothing and no#include
directive will be added.