A string formatting library in 65 lines of C++

38 pointsposted 6 hours ago
by PaulHoule

15 Comments

merlincorey

an hour ago

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.

See:

[1] https://en.cppreference.com/w/cpp/utility/format/format.html

[2] https://en.cppreference.com/w/cpp/io/print.html

hoten

an hour ago

Is it in major compilers yet? Last I checked for MSVC it was behind a "latest" compiler flag (not C++20). I've been vendoring the fmt library for awhile now.

shakna

24 minutes ago

From GCC 13, and clang 17. (2023).

Unfortunately, MSVC, always lags and fails to implement some things.

kevin_thibedeau

6 hours ago

  char buffer[64];
  String_Buffer buf = {str, sizeof str};
Probably meant the "buffer" to be "str" here.

thw_9a83c

6 hours ago

Clearly yes. BTW, I don't see a benefit to use a non-owning String_Buffer over std::string (or std::string_view) in this context.

kevin_thibedeau

5 hours ago

The subtext is a resource constrained system where std::format is considered too heavyweight. In that scenario, explicit non-automatic memory management is a benefit. It could still leverage std::string_view and be agnostic on the topic.

dpmdpm

5 hours ago

I prefer https://github.com/rokudev/rostd/blob/main/doc/printx.adoc, but it does increase compile times (which OP was trying to avoid).

o11c

3 hours ago

If you're willing to use one measly little macro - solely to smuggle the format string in a constexpr manner - instead of insisting on using templates everywhere, you can use a printf wrapper with essentially 0 compile-time overhead. And the only runtime overhead is if you have to copy a `string_view` back into a `string` to add the `NUL`-terminator.

You do still need templates for the arguments (unless you're willing to resort to nasty preprocessor hackery, which would be needed if doing this in C - hmm, are the lifetime-of-temporary rules different too?), but it's pretty easy to just do:

  my_asprintf_or_whatever(to_borrowed_primitive(to_owning_primitive(arg))...)
where `to_owning_primitive` is the ADL'ed function you implement for every type you want to print, and `to_borrowed_primitive` probably only needs to be implemented for each string type (though I did find it also useful for wrapped integers of unknown size/rank, such as `time_t`).

worstenbrood

5 hours ago

Love the method name uhm bool next_hole

vjvjvjvjghv

5 hours ago

I much prefer string interpolation like

$"i={i}"

secondcoming

3 hours ago

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.

cppisnice

6 hours ago

How many CVEs?

speed_spread

4 hours ago

Yes, true. But the probability of finding new CVEs from any 65 lines of non-obfuscated code diminishes rapidly. In many situations I'd rather use a short minimal fresh lib that I can review as if it was mine than a mature but overly feature-loaded one that may still have any number of pending gotchas in dark corners.

prerok

3 hours ago

I must admit I was very much against the practice of NIH syndrome, but if it's that short I would prefer to write my own version instead of adding a dependency.

In this day and age who knows when a dependency is hijacked :(

speed_spread

2 hours ago

At 65 lines, if the license is right, you can just copy it like you would with a StackOverflow answer. In these situations I leave a comment on top saying where the code came from so it can be revisited later.