Nice. I think most people have tried doing something like this in C++ at some point.
One issue that I had is that printing floating-point values really needs the ability for the user to specify the precision and format. It's actually absurd that `std::to_string(double)` does not allow this.
Also, I believe `std::to_chars(double)` uses a fast algorithm and allows writing directly into a buffer.
There's a section on "why not printf" which is Standard C, but I can't find any section on "why not std::format"[1] which is Standard C++ since C++20 and works on all major compilers today in 2025.
They do mention "std::print"[2] from C++23 (which uses std::format) and compile times, but, they don't touch on "std::format" at all.
Pretty neat and a very nice walkthrough of the code.
For localization you might want numbered holes which makes it way more complicated.
You can detect if the backing buffer is too short, but can you detect other errors? Like having different numbers of holes and arguments? I couldn’t find any discussion about this.
I was curious about the "Why not printf" section, but I found code I don't understand there, too. For example this admittedly non-working snippet is cited as idiomatic:
Of corse this doesn't work (if the intent was to assemble the "hello world!" string, of which I'm not entirely sure), but not for the reason stated in TFA. You need to actually use cursor, not merely set it! :)
I have recently started re-implementing parts of the standard library myself just to improve compile times (and I did - massively!), but I purposely kept {fmt} around, because I think it's a great library and I thought it would be as fast to compile as you could possibly make it (it likely still is considering what it can do). Also because the dev put a lot of effort into optimizing compile times [1] and seems to be much smarter than me. So I made benchmark to prove you wrong and show you it's not that easy. But it turns out formatting a couple of numbers and strings is much faster with your basic formatting library [2] [3].
Comparing using `hyperfine --warmup 3 "g++ FILE.cpp"` (and some flags for fmt) I get 72ms vs 198ms. So I changed my mind and might take a crack at replacing {fmt} as well. Cool stuff! Thank you.
Note that {fmt} provides a lightweight API (`fmt/base.h`) with a superset of `printf` functionality and optimized for build speed (https://vitaut.net/posts/2024/faster-cpp-compile-times/) and binary size (https://vitaut.net/posts/2024/binary-size/) through type erasure. Variadic templates, while OK in simple cases, won't scale for more realistic ones (especially once you start adding support for more types) and won't work even for basic things like positional arguments.
13 comments
[ 2.2 ms ] story [ 38.6 ms ] thread$"i={i}"
One issue that I had is that printing floating-point values really needs the ability for the user to specify the precision and format. It's actually absurd that `std::to_string(double)` does not allow this.
Also, I believe `std::to_chars(double)` uses a fast algorithm and allows writing directly into a buffer.
They do mention "std::print"[2] from C++23 (which uses std::format) and compile times, but, they don't touch on "std::format" at all.
See:
[1] https://en.cppreference.com/w/cpp/utility/format/format.html
[2] https://en.cppreference.com/w/cpp/io/print.html
The kinds of places still waiting C++ aren’t usually the ones that put much emphasis on using a compiler from the past decade.
Java 8 and C++98 will be here forever lol
For localization you might want numbered holes which makes it way more complicated.
You can detect if the backing buffer is too short, but can you detect other errors? Like having different numbers of holes and arguments? I couldn’t find any discussion about this.
I was curious about the "Why not printf" section, but I found code I don't understand there, too. For example this admittedly non-working snippet is cited as idiomatic:
Of corse this doesn't work (if the intent was to assemble the "hello world!" string, of which I'm not entirely sure), but not for the reason stated in TFA. You need to actually use cursor, not merely set it! :)Comparing using `hyperfine --warmup 3 "g++ FILE.cpp"` (and some flags for fmt) I get 72ms vs 198ms. So I changed my mind and might take a crack at replacing {fmt} as well. Cool stuff! Thank you.
[1] https://vitaut.net/posts/2024/faster-cpp-compile-times/
[2] https://godbolt.org/z/3YaovhrjP bench-fmt.cpp
[3] https://godbolt.org/z/qMfM39P3q bench-rikifmt.cpp