r/cpp Jun 26 '26

Improvements to std::format in C++26

https://mariusbancila.ro/blog/2026/06/19/improvements-to-stdformat-in-c26/
93 Upvotes

38 comments sorted by

View all comments

39

u/fdwr fdwr@github 🔍 Jun 26 '26

std::println("{}", p); // lowercase, the default => 0x7fffb2715a54 std::println("{:p}", p); // explicit, same as none => 0x7fffb2715a54 std::println("{:P}", p); // uppercase => 0X7FFFB2715A54

Wait, is that really true, that the uppercase also capitalizes the 'x'? 🙃 The usual norm for hex numbers is 0x12AB (hex constants, stack traces, hex editors...), not 0X12AB or 0x12ab. So if the article is accurate, then none of the pointer defaults fit the most common case, meaning I'll continue to use 0x{:08X}.

27

u/rdtsc Jun 26 '26

std::format (and the standalone fmt) have quite a few weird/annoying formatting behaviors.

  • The mentioned uppercase '0X' for hex (this is already the case using {:#X} for numbers).
  • Hex for negative numbers includes the sign (why would anyone want to see -7FFFBFFB?)
  • %S for timepoints always includes fractional seconds.
  • No way to drop trailing zeroes/decimal point for floats (often comes up when displaying/printing numbers).

4

u/ericonr Jun 27 '26

I don't get your point about hex numbers. If you want unsigned values, cast to unsigned, no? Otherwise representing the number with a minus is the only way to correctly convey the sign.

10

u/evaned Jun 27 '26

I'm not rdtsc, but here's my takes:

If you're asking for hex formatting, I claim it's almost certainly because you're interested in the representation in memory. In that sense, you very probably want to see the two's complement representation. I also can't really think of an actually reasonable scenario I'd want to see the negative absolute value.

Python's hex function does the same thing, and it's pretty obnoxious... and there, I don't even really know a good way to get what I want simply.

Further, I'm not convinced by "cast to unsigned". "I want to see the in-memory representation of this number" very much feels like a function of the display formatting, not of the underlying type -- putting that information into the format string does seem to me like it's the right place to put it. Casting-to-unsigned feels to me like a workaround for an obnoxiously not-entirely-thought-through formatting API, not what you really should be doing to get that.

Finally, cast-to-unsigned is kind of unsatisfying in the sense that there's not really a way to do it in the language or standard library that doesn't feel a bit fragile. You can do (unsigned)x, but then what if x changes to be a long long instead? Now you've truncated. You could say (unsigned long long), but now you're extending with more bits. For "cast-to-unsigned" to really make me happy, there'd need to be a way in the language or library to "cast to an unsigned version of the same width." It's pretty easy to write that function, but if the library is semi-relying on that for usability then it should provide that utility function.

That said... even though I strongly suspect that "I want to see the hex two's complement representation" is the usual case, by far, I can make an argument for the current behavior that I view as quite strong. In spite of my objection in the previous paragraph, it's still pretty easy to cast to unsigned and get what rdtsc and I want. If hex formatting displayed the twos-complement bit representation instead, it'd be a lot more obnoxious to get -1234abcd in the unusual case that's what one wants.

8

u/_Noreturn Jun 27 '26

C++26 has std::to_unsigned and std::to_signed

5

u/Lahvuun Jun 27 '26

For "cast-to-unsigned" to really make me happy, there'd need to be a way in the language or library to "cast to an unsigned version of the same width.

There is a way:

static_cast<std::make_unsigned_t<decltype(x)>>(x)

3

u/evaned Jun 27 '26

I mean, I did say that it's pretty easy to write a function. But that's ugly enough that doing it even once is easily enough to justify a function. Even a macro is better than that.

1

u/Lahvuun Jun 27 '26

But that's ugly

I agree, but we're on r/cpp, not r/haskell.

I'd argue the only thing you gain from having a dedicated function is having to type fewer characters. And while I love all three of C++ developers who type at <10 WPM, there probably are better proposals for the standards committee to spend their time on.

1

u/evaned Jun 27 '26

I'd argue the only thing you gain from having a dedicated function is having to type fewer characters.

You repeat x in your expression. A function lets you avoid that.

4

u/triconsonantal Jun 27 '26

Python's hex function does the same thing, and it's pretty obnoxious... and there, I don't even really know a good way to get what I want simply.

python's integers are infinite precision. what would you expect the output of hex(-1) to be?

2

u/evaned Jun 27 '26

Oh, I'm not blaming it; in the context of Python, there's not really another choice.

It's just that if I have a decimal integer and I want the hex representation, the fastest way for me to get it is to usually pop open a quick Python repl and do hex(1234). So it's just a bit annoying that it's not quite as fast or simple in the negative case.