Modify rules S6185,S6484,S6494,S6495: RSPEC improvements (CPP-5056) (#3816)

This commit is contained in:
Marco Borgeaud 2024-03-27 11:58:36 +01:00 committed by GitHub
parent 18f43c5f7f
commit b46c38154d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 104 additions and 54 deletions

View File

@ -9,7 +9,7 @@ Before {cpp}20, one popular way to obtain the same result was the conversion of
``++std::format++`` is strictly superior. It is more efficient because it constructs the string in-place instead of copying substrings one by one. It is also often shorter and easier to read because the format pattern is presented in a single piece and not scattered across the concatenation expression.
This rule reports string concatenation cases that can be replaced by ``++std::format++`` and gain in speed and readability.
This rule reports string concatenation cases that can be replaced by ``++std::format++`` to improve performance and readability.
=== Noncompliant code example
@ -31,3 +31,19 @@ std::string greeting(int n) {
}
----
== Resources
=== Documentation
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/format/format[`std::format`]
* {cpp} reference - https://en.cppreference.com/w/cpp/string/basic_string/to_string[``++std::to_string++``]
=== Articles & blog posts
* {cpp} Stories - https://www.cppstories.com/2022/custom-stdformat-cpp20/[Formatting Custom types with std::format from {cpp}20]
=== Related rules
* S6484 - Concatenated "std::format" outputs should be replaced by a single invocation
* S6494 - {cpp} formatting functions should be used instead of C printf-like functions
* S6495 - "std::format" should be used instead of standard output manipulators

View File

@ -1,12 +1,12 @@
== Why is this an issue?
`std::format` accepts a format string composed of ordinary text and replacement fields (surrounded with `{}`) that are replaced with a textual representation of the next `std::format` arguments.
`std::format` accepts a format string composed of ordinary text and replacement fields (surrounded with `{}`) that are replaced with a textual representation of the remaining `std::format` arguments.
This allows generating a complex string with a single invocation of `std::format`.
Since calls to `std::format` produce string objects, it is possible to concatenate them with other string objects or string literals.
However, compared to a single `std::format` invocation with an adjusted format string, this concatenation is inefficient and less readable.
This rule raises an issue when a concatenation an `std::format` invocation can be replaced with a simple `std::format` invocation.
This rule raises an issue when the concatenation performed on the result of `std::format` can be replaced with a single `std::format` invocation.
=== Noncompliant code example
@ -16,6 +16,16 @@ void formatExamples(std::string str, char const* cstr, int i) {
std::string s1 = "You have been greeted " + std::format("{}", i) + " times."; // Noncompliant
std::string s2 = "Hello " + std::format("{:*^20}", str) + "! " + std::format("{:->15}", cstr) + '.'; // Noncompliant
}
----
=== Compliant solution
[source,cpp]
----
void formatExamples(std::string str, char const* cstr, int i) {
std::string s1 = std::format("You have been greeted {} times.", i); // Compliant
std::string s2 = std::format("Hello {:*^20}! {:->15}.", str, cstr); // Compliant
}
std::string fullName(std::string name, std::string secondName, std::string surname, std::size_t number) {
// Compliant, as the formatted output depends on runtime properties
@ -29,14 +39,18 @@ std::string fullName(std::string name, std::string secondName, std::string surna
}
----
=== Compliant solution
== Resources
[source,cpp]
----
void formatExamples(std::string str, char const* cstr, int i) {
std::string s1 = std::format("You have been greeted {} times.", i); // Compliant
std::string s2 = std::format("Hello {:*^20}! {:->15}.", str, cstr); // Compliant
std::string s3 = std::format("Welcome {:*^20}! {:->15}.", str, cstr); // Compliant
}
----
=== Documentation
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/format/format[`std::format`]
=== Articles & blog posts
* {cpp} Stories - https://www.cppstories.com/2022/custom-stdformat-cpp20/[Formatting Custom types with std::format from {cpp}20]
=== Related rules
* S6185 - "std::format" should be used instead of string concatenation and "std::to_string"
* S6494 - {cpp} formatting functions should be used instead of C printf-like functions
* S6495 - "std::format" should be used instead of standard output manipulators

View File

@ -2,13 +2,10 @@
In contrast to C printf-like functions, {cpp} provides safer and more robust interfaces for performing text formatting:
* The `std::format` interface family ({cpp}20) allows formatting text into a string
* The `std::print` interface family ({cpp}23) allows printing formatted text
* The `std::format` interface family ({cpp}20) allows formatting text into a string.
* The `std::print` interface family ({cpp}23) allows printing formatted text.
Firstly, {cpp} 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::print` and `std::format`.
In addition `std::vformat`, `std::vprint_unicode`, and `std::vprint_nonunicode` report validation failures by throwing an instance of `std::format_error`.
{cpp} 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::print` and `std::format`. When the format string is not available at compile-time, `std::vformat`, `std::vprint_unicode`, and `std::vprint_nonunicode` can be used. They will report failures at runtime by throwing an instance of `std::format_error`.
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.
@ -17,8 +14,7 @@ Furthermore, `{}` can be used for any type with default format spec, which makes
Finally, the text formatting API was designed with adaptability in mind:
* Formatting of user-defined types is possible with the dedicated format specification via
`std::formatter` specializations.
* Formatting of user-defined types is possible with the dedicated format specification via `std::formatter` specializations.
* The string formatting API provides functions for:
- receiving the formatted text by return - `std::format`.
@ -35,15 +31,18 @@ This rule raises issues for calls of the `printf`, `fprintf`, `sprintf` and `snp
[source,cpp]
----
void printFunc(char* out, size_t n) {
sprintf(out, "%u %s", 10u, “text”); // Noncompliant
void printTextIntoBuffer(char* out) {
// Assumes the buffer pointed-to by out is large enough
sprintf(out, "%u %s", 10u, "text"); // Noncompliant
}
void printTextIntoSizedBuffer(char* out, size_t n) {
std::snprintf(out, n, "%i %% %LG", 10, 10.0L); // Noncompliant
}
void printFile(FILE* f) {
// Since C++23
printf("%i", 10); // Noncompliant
std::fprintf(f, "%f", 10.0); // Noncompliant
void printToFile(FILE* f) {
printf("%i", 10); // Noncompliant since C++23
std::fprintf(f, "%f", 10.0); // Noncompliant since C++23
}
----
@ -51,18 +50,21 @@ void printFile(FILE* f) {
[source,cpp]
----
void printFunc(char* out) {
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
void printTextIntoBuffer(char* out) {
// Assumes the buffer pointed-to by out is large enough
std::format_to(out, "{} {}", 10u, "text"); // Compliant
}
// The function can also be redesigned to deal with memory allocation
// and return a string:
std::string getText() {
return std::format("{} {}", 10u, "text"); // Compliant
}
void printFile(FILE* f) {
// Since C++23
void printTextIntoSizedBuffer(char* out, size_t n) {
std::format_to_n(out, n, "{} % {:G}", 10, 10.0L); // Compliant
}
void printToFile(FILE* f) {
std::print("{}", 10); // Compliant
std::print(f, "{}", 10.0); // Compliant
}
@ -70,8 +72,7 @@ void printFile(FILE* f) {
=== Exceptions
The rule does not raise an issue if the format string passed to a printf-like function is computed dynamically
instead of being spelled in the source code:
The rule does not raise an issue if the format string passed to a printf-like function is computed dynamically instead of being spelled in the source code:
[source,cpp]
----
@ -80,20 +81,24 @@ char const* localizedFormatString(unsigned id);
snprintf(buffer, localizedFormatString(123), 10, 20)
----
While `std::vformat` may be used in such cases, it would require a change of the format string,
which may not be actionable.
While `std::vformat` may be used in such cases, it requires changing the format string, which may not be actionable.
== Resources
=== Documentation
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/format/format[`std::format`]
* {cpp} reference - https://en.cppreference.com/w/cpp/io/print[`std::print`]
* {cpp} reference - https://en.cppreference.com/w/cpp/header/format[`<format>`]
* {cpp} reference - https://en.cppreference.com/w/cpp/header/print[`<print>`]
* {cpp} reference - https://en.cppreference.com/w/cpp/io/basic_ostream/print[`std::print(std::ostream)`]
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/format/format_to[`std::format_to`]
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/format/format_to_n[`std::format_to_n`]
* {cpp} reference - https://en.cppreference.com/w/cpp/io/vprint_unicode[`std::vprint_unicode`]
* {cpp} reference - https://en.cppreference.com/w/cpp/io/vprint_nonunicode[`std::vprint_nonunicode`]
* {cpp} reference - https://en.cppreference.com/w/cpp/io/basic_ostream/vprint_unicode[`std::vprint_unicode(std::ostream)`]
* {cpp} reference - https://en.cppreference.com/w/cpp/io/basic_ostream/vprint_nonunicode[`std::vprint_nonunicode(std::ostream)`]
=== Articles & blog posts
* {cpp} Stories - https://www.cppstories.com/2022/custom-stdformat-cpp20/[Formatting Custom types with std::format from {cpp}20]
=== Related rules
* S6185 - "std::format" should be used instead of string concatenation and "std::to_string"
* S6484 - Concatenated "std::format" outputs should be replaced by a single invocation
* S6495 - "std::format" should be used instead of standard output manipulators

View File

@ -1,13 +1,11 @@
== Why is this an issue?
{cpp}20 introduces a new text formatting API with the ``<format>`` header,
joining the ``printf`` family of functions -- inherited from C -- and ``iostreams``.
``std::format`` combines the convenience of ``printf``, separating formatting and
arguments, with the type-safety of ``iostreams``.
{cpp}20 introduces a new text formatting API with the ``<format>`` header, in addition to the ``printf`` funciton family -- inherited from C -- and ``iostreams``.
``std::format`` combines the convenience of ``printf``, separating formatting and arguments, with the type-safety of ``iostreams``.
Before {cpp}20, if you wanted to format an output stream, you had to use standard manipulators that control the output streams.
This approach is very verbose, is often stateful, and is not thread-safe. That is why we recommend replacing them with ``std::format``
when possible.
This approach is very verbose, is often stateful, and is not thread-safe.
That is why we recommend replacing them with ``std::format`` when possible.
Some manipulators will have a temporary effect on the output. For example, ``std::setw``. This is due to the resetting of the width property of the stream when most of the ``operator<<`` is called.
Other manipulators will have a lasting effect on the output. For example, ``std::boolalpha``. It will set the ``boolalpha`` flag of the output stream without resetting it.
@ -71,3 +69,20 @@ void printQuoted(std::string_view s) {
std::cout << std::quoted(s, '$', '-');
}
----
== Resources
=== Documentation
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/format/format[`std::format`]
* {cpp} reference - https://en.cppreference.com/w/cpp/header/iomanip[`<iomanip>`]
=== Articles & blog posts
* {cpp} Stories - https://www.cppstories.com/2022/custom-stdformat-cpp20/[Formatting Custom types with std::format from {cpp}20]
=== Related rules
* S6185 - "std::format" should be used instead of string concatenation and "std::to_string"
* S6484 - Concatenated "std::format" outputs should be replaced by a single invocation
* S6494 - {cpp} formatting functions should be used instead of C printf-like functions