eupolicy.social is one of the many independent Mastodon servers you can use to participate in the fediverse.
This Mastodon server is a friendly and respectful discussion space for people working in areas related to EU policy. When you request to create an account, please tell us something about you.

Server stats:

221
active users

#cplusplus

41 posts14 participants4 posts today
Sandor Dargo’s Blog · C++26: std::format improvements (Part 2)In Part 1, we explored the improvements C++26 brings to std::format — from better to_string behavior to compile-time safety checks. In this part, we look at runtime formatting, defect fixes, and support for new types like std::filesystem::path. Runtime format strings P2216R3 brought quite some improvements to std::format, including compile-time checking for format strings. Sadly, in use cases where format strings were only available at runtime, users had to go with the type-erased formatting version, std::vformat: 1 std::vformat(str, std::make_format_args(42)); Using two different APIs is not a great user experience, moreover, std::vformat was designed to be used by formatting function writers and not by end users. In addition, you might run into undefined behaviour, detailed in the next section. To overcome this situation, P2918R2 adds std::runtime_format so you can mark format strings that are only available at run-time. As such you can opt out of compile-time format strings checks. This makes the API cleaner and the user code will read better as it shows better the intentions. 1 2 3 4 5 // Before: std::vformat(str, std::make_format_args(42)); // After: std::format(std::runtime_format(str), 42); This change is already available in GCC 14 and Clang 18. DR20: std::make_format_args now accepts only lvalue references instead of forwarding references P2905R2 fixes unintended consequences of P2216R3 std::format improvements. It offered checking format strings at compile-time (as we’ve just seen), so in use cases where format strings were only available at runtime, users had to go with the type-erased formatting version: std::vformat. The problem is that in innocent-looking code, like below, users face undefined behaviour as format arguments store references to temporaries which are destroyed before use: 1 2 3 4 std::string str = "{}"; std::filesystem::path path = "path/etic/experience"; auto args = std::make_format_args(path.string()); std::string msg = std::vformat(str, args); The fix is that std::make_format_args should take lvalue references instead of forwarding references, rejecting code like the above. It’s been already implemented in fmt and this is knowingly and deliberately a breaking change. Luckily, this defectous feature hasn’t been widely used. This fix is already available in all the three major compilers, GCC 14, Clang 18 and MSVC 19.40. DR20: Fix formatting of code units as integers P2909R4 fixes a defect report. An earlier proposal introduced a bug into how chars are formatted. Whether char is signed or unsigned is implementation defined and std::format (through std::to_chars) always promotes a char to an int - which is always signed. As char is always used as a code unit type in std::format (and in other text processing facilities) - and a sometimes signed integer output has been surprising to the users. Here is an example: 1 2 3 4 5 6 7 8 9 10 11 for (char c : std::string("🤷")) { std::print("\\x{:02x}", c); } /* output is either this \xf0\x9f\xa4\xb7 or this \x-10\x-61\x-5c\x-49 */ The fix proposed by the author, Victor Zverovich, is to always convert a character type to the unsigned version of it when it’s getting formatted. Though it’s not the goal, it results in the same behaviour as printf has. This fix is already available in all the three major compilers, GCC 13.3, Clang 18 and MSVC 19.40. std::formatter<std::filesystem::path> Thanks to P2845R8m we will be able to print our standard paths nicely formatted. Wasn’t that available already? Not yet. Although P1636 already proposed formatters for library types, std::filesystem::path was removed because of some issues which are solved by now by the current proposal. The previously proposed formatter always printed paths quoted. That is not always suitable for paths, notably when the path is a multiline one: 1 2 3 4 5 6 std::cout << std::format("{}", std::filesystem::path("multi\nline")); /* The below output is not a valid C++ string "multi line" */ In the end end, a formatter is being added as per P2286, and by default it doesn’t print paths quoted. But it also adds a “debug format specifier” where control characters are escaped and the output is quoted. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 auto p = std::filesystem::path("/usr/bin"); std::cout << std::format("{}", p); // output: // /usr/bin auto p = std::filesystem::path("multi\nline"); std::cout << std::format("{}", p); // output: // multi // line auto p = std::filesystem::path("multi\nline"); std::cout << std::format("{:?}", p); // output: // "multi\nline" The other problem was UTF-8 encoding on certain platforms when intermediary conversion are performed. The problem has been earlier solved by P2093R14 which is now applied here as well. As a result paths with UTF-8 as a literal encoding will be printed as one would expect. This change is not yet available in any of three major compilers. Conclusion C++26 makes std::format more robust with safer, and cleaner API. The changes might look small in isolation, but together, they significantly improve the day-to-day developer experience. If you missed Part 1, check it out here. Connect deeper If you liked this article, please hit on the like button, subscribe to my newsletter and let’s connect on Twitter!