Closed mmahnic closed 2 years ago
I have a suggestion for a future version that is related to this issue.
Formatting could be used in different scenarios, not only for displaying strings on a terminal. I think it would be useful if it would be possible to specify what the format width means. The standard specifies that for Unicode strings the width should be calculated from extended grapheme clusters, which is useful for displaying. But in other scenarios, it might be more useful to specify the width in bytes or code points.
For example:
// Format to 3 bytes
fmt::format("{:*>3b}", u8string("\xC4\x8D")) // 2a c4 8d
// Format to 3 code points (*, LATIN SMALL LETTER C WITH CARON, CARON)
fmt::format("{:*>3c}", u8string("\xC4\x8D\xCB\x87")) // 2a c4 8d cb 87
// Format to 3 extended grapheme clusters; this is the default.
fmt::format("{:*>3g}", u8string("\xC4\x8D\xCB\x87")) // 2a 2a c4 8d cb 87
{fmt} assumes UTF-8 encoding by default. If you want to opt into a legacy encoding/binary wrap the argument in fmt::bytes
, e.g.
auto s = fmt::format("{:*>3}", fmt::bytes("\xC4\x8D"));
This is unfortunate. We'd have to revise the occurrences of fmt::format in our whole codebase, around 500k lines. For now, we will keep using 6.1.2, then we'll move to C++20 and std::format where it looks like no encoding is assumed for raw strings and std::string.
If a std::string contains characters with the high bit set, formatting to N places creates strings that are too large. This used to work correctly in version 6.1.2. At least with the Visual C++ 2022 compiler, the behavior of fmt::format is different than the behavior of std::format.
It looks like the formatter is trying to treat the input sequence as UTF-8. Although the standard explicitly states that the calculation of the width of strings without encoding is unspecified, and every implementation could use a different algorithm, I think the formatter should do as little as possible and not interpret the contents of a std::string, because it can contain any encoding. Interpretation should be done only for strings where the encoding is explicitly defined by the character type.
Here are some examples followed by the output on a Windows machine with the C locale.