diff --git a/rules/S6185/cfamily/rule.adoc b/rules/S6185/cfamily/rule.adoc index 67328782c9..c292b1a5a7 100644 --- a/rules/S6185/cfamily/rule.adoc +++ b/rules/S6185/cfamily/rule.adoc @@ -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 diff --git a/rules/S6484/cfamily/rule.adoc b/rules/S6484/cfamily/rule.adoc index 8e313ae64a..89f669045c 100644 --- a/rules/S6484/cfamily/rule.adoc +++ b/rules/S6484/cfamily/rule.adoc @@ -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 diff --git a/rules/S6494/cfamily/rule.adoc b/rules/S6494/cfamily/rule.adoc index 1c48db8031..9fc6d1a640 100644 --- a/rules/S6494/cfamily/rule.adoc +++ b/rules/S6494/cfamily/rule.adoc @@ -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[``] +* {cpp} reference - https://en.cppreference.com/w/cpp/header/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 diff --git a/rules/S6495/cfamily/rule.adoc b/rules/S6495/cfamily/rule.adoc index 5646422965..4cfcb602bf 100644 --- a/rules/S6495/cfamily/rule.adoc +++ b/rules/S6495/cfamily/rule.adoc @@ -1,13 +1,11 @@ == Why is this an issue? -{cpp}20 introduces a new text formatting API with the ```` 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 ```` 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[``] + +=== 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