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 at or set to their default values then this check is only enabled with -std=c++23 or later.

Macros starting with PRI and __PRI from <inttypes.h> are expanded, escaping is handled and adjacent strings are concatenated to form a single StringLiteral before the format string is converted. Use of any other macros in the format string will cause a warning message to be emitted and no conversion will be performed. The converted format string will always be a single string literal.

The check doesn’t do a bad job, but it’s not perfect. In particular:

If conversion would be incomplete or unsafe then the entire invocation will be left unchanged.

If the call is deemed suitable for conversion then:

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 and ssize_t) as the opposite signedness to ensure that the output matches that of printf. This does not apply when converting from non-variadic functions such as absl::PrintF and fmt::printf. For example, with StrictMode enabled:

int i = -42;
unsigned int u = 0xffffffff;
printf("%u %d\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. Qualified member function names are supported, but the replacement function name must be unqualified. 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. Qualified member function names are supported, but the replacement function name must be unqualified. 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 default std::print when the originalformat string does not end with \n. It is expected that the function provides an interface that is compatible with std::print. A suitable candidate would be fmt::print.

ReplacementPrintlnFunction

The function that will be used to replace printf, fprintf etc. during conversion rather than the default std::println when the original format string ends with \n. It is expected that the function provides an interface that is compatible with std::println. A suitable candidate would be fmt::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 is std::print then this option will default to <print>, otherwise this option will default to nothing and no #include directive will be added.