Why is this an issue?
In contrast to C printf-like functions, the std::format
family of formatting functions provides a safer, and more robust interface for
performing text formatting.
Firstly, C++ formatting facilities perform validation of the format string against the type of the formatted argument. If the validation fails, it
is reported as a compilation error for the calls of std::format
, and via exception for std::vformat
.
Secondly, the relation between the type and format specifier is more abstract. In particular, {:d}
can be used to format any integer
type, regardless of its size and signedness. Similarly, {:f}
works for any floating point type. Furthermore, {}
can be used
for any type with default format spec, which makes it usable in the generic context.
Finally, the std::format
API can be extended to support custom types with the dedicated format specification via
std::formatter
specializations.
This rule raises issues for calls of the sprintf
and snprintf
functions that can be replaced by the C++ formatting
functions.
Noncompliant code example
void printFunc(FILE* f, char* out, size_t n) {
sprintf(out, "%u %s", 10u, “text”); // Noncompliant
std::snprintf(out, n, "%i %% %LG", 10, 10.0L); // Noncompliant
printf("%i", 10); // Compliant, no direct remplacement
std::fprintf(f, "%f", 10.0); // Compliant, no direct remplacement
}
Compliant solution
void printFunc(FILE* f, char* out, size_t n) {
std::format_to(out, “{:d} {:s}”, 10u, text); // Compliant
// or
std::format_to(out, “{} {}”, 10u, text); // Compliant
std::format_to_n(out, ”{:d} % {:G}”, 10, 10.0L); // Compliant
// or
std::format_to_n(out, ”{} % {:G}”, 10, 10.0L); // Compliant
printf("%i", 10); // Compliant, no direct remplacment
std::fprintf(f, "%f", 10.0); // Compliant, no direct remplacment
}
Exceptions
The issue is raised if the format string passed to printf-like function is computed dynamically, instead of being spelled in the source code:
char const* localizedFormatString(unsigned id);
/* …. */
snprintf(buffer, localizedFormatString(123), 10, 20)
While std::vformat
may be used in such cases, it will require an change of the format string, which may not be actionable.