Live data from Hacker News

Three new utility functions in C++23

mariusbancila.ro

31–40 of 196 posts

Re: Three new utility functions in C++23

#31
post #5

I love how over the past decade my own C++ utility library has been continuously shrinking because with each update there are more and more utility functions (like the to_underlying this article mentions) and even complete libraries (like ) which replace self-written or 3rd party code.

I always worry as much as anyone else on each new release for the additional complexity ("the committee is out of control!!!!1!!eleven!"), but on each compiler upgrade when I actually get to use the new versions of the standard I'm always pleasantly surprised about all the little low-key quality of life improvements.

While that's true, when you get to a point where you have to write a small library - so that you need to cater to all of the language and standard additions - that's when you start experiencing pain. How do I expose the right iterators and sentinels? What do I have to specialize? Do I need to define concepts? Do I need to use concepts from elsewhere? I am often at a loss...

Re: Three new utility functions in C++23

#32
post #6

Hmm. How often do people actaully want to std::byteswap as opposed to "convert this value from native byte order to big-endian" or "convert this value from little-endian to native byte order"? i.e., the functions documented in https://man7.org/linux/man-pages/man3/endian.3.html (why oh why are they not also documented in the GNU C Library Manual...)

Yeah, for portable code you also need a function to tell you if you're on an architecture where you need to do a byteswap for the data you have. e.g. you know you have data in little-endian format - do you need to swap it to work with it natively? That depends. Maybe having something like convert_be() and convert_le(), one of which is a no-op and the other does the byteswap (depending on your arch) would be better. I…

Huh. You know I had never considered why we have htobe32 and be32toh which do exactly the same thing. Maybe because of oddball architectures which are neither big nor little endian?

For instance... /usr/include/x86_64-linux-gnu/bits/endian.h has:

#define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #define __PDP_ENDIAN 3412

Apparently this is called 'middle-endian'... yikes!

On the other hand I sort of like having one function that's clearly used for importing values to the host's byte order, and another for exporting values from the host's byte order. But maybe that's just because I'm used to having them...

Re: Three new utility functions in C++23

#33

I understand why it's there, but I do find it fun that when many people are trying to reduce undefined behaviour in their code, std::unreachable is literally defined as "this is undefined behaviour, use that to optimise". I suspect 99.9% of uses of std::unreachable would be better replaced by abort. (There will be those times when the code is correct and the optimisation gains are worth it -- but they will be rare).

Then just use abort!

Re: Three new utility functions in C++23

#34
post #12

Why is it called "byteswap" and not "bytereverse"? "Swapping" can be done in any number of ways, but only one way to reverse.

I don't know, but 'swap' seems to have become the standard term for the operation, used in places like the C bswap family of functions and the x86 'bswap' instruction. My (totally unsupported and unresearched) guess is that it became popular as a term when 16-bit architectures were common -- "swap the bytes in a 16 bit value" is unambiguous.

The Arm architecture does call this operation "reverse bytes", though, so it's not universal to call it "swap".

Re: Three new utility functions in C++23

#35

Earlier quoted context omitted.

+100 I felt physically ill when I read: It’s intended to be used when you know you have an execution path in your code that cannot be reached but the compiler cannot figure that out. It felt like saying to the compiler, "please, find a way to make my program break even more easily". Exactly not what I need.

If a code path that's supposed to be unreachable is reached then the program is already broken. Unless it has a bug, a compiler will not make a program more broken. At worst (or best, depending on how you look at it) it will only make any bugs it already has more obvious.

> At worst (or best, depending on how you look at it) it will only make any bugs it already has more obvious.

An abort would make them obvious, surely unreachable makes them less obvious?

Re: Three new utility functions in C++23

#36
My first impression is that all three of these are clutter to an already very cluttered language ...

1.) Coming from embedded programming, I can see the utility of `std::unreachable`. But shouldn't this be a compiler directive? Or a standardized #pragma? Can someone more knowledgeable in C++ say whether using functions as markers is a common mechanism in std:: ?

2.)Maybe the example is bad here, as it doesn't even save typing. (18 chars for `std::to_underlying` vs. 16 for `static_cast`. The 'old' variant seems more expressive to boot. If anything, how about `static_cast`? or ...

3.) The usefulness of `std::byteswap` to convert data to network byte order seems trivial vs. the venerable old `htonl` family of functions. `std::byteswap` seems more like intrinsics meant to expose possibly present target machine instructions to the user. Like `std::unreachable` this is probably of most use to embedded / low-level programming. This may be a deficiency in the article...

If it's not obvious, I'm not a big C++ fan and read the article with C-tinted glasses :)

Re: Three new utility functions in C++23

#37
post #7
post #3

>Network protocols specify big endian for the order of transmission Only in the parts specified by the protocol (headers etc). I encourage everyone sending data over network in a novel way to just use little-endian.

That's not a good advice. Only if the sender and receiver are guaranteed to be running on little endian architecture you can make such a claim. A better advice is to always consider the endian-ness when designing protocols and have a strategy to handle it.

There are practically no big endian architectures anymore. Little endian is a sensible default. The weird architectures should bare the burden of complexity.

Re: Three new utility functions in C++23

#38

Earlier quoted context omitted.

It's undefined behavior. The compiler is free to do whatever, including making your program more broken.

If you have undefined behavior, your program is already broken. No such thing as "more broken"; there's already no theoretical limit to what might happen if it gets triggered.

Undefined behavior is considered worse than crashing, which is typically the alternative when reaching "unreachable" codepaths.

Compare these two blocks similar to the article

   switch (ch) {
   case 'a': do_a(); return;
   case 'd': do_d(); return;
   // ch is guaranteed to be 'a' or 'd' by previous code.
   default: assert(0);
   }

   switch (ch) {
   case 'a': do_a(); return;
   case 'd': do_d(); return;
   default: std::unreachable();
   }

If the programmer is wrong about `ch` in the first one, the program terminates. For the second one, the compiler could change it to be equivalent to

  if (ch == 'a') { do_a(); }
  else { do_d(); }
  return;
If the programmer is wrong here, the program might `do_d()` with unintended consequences. I'd say "going down unintended codepaths" is typically considered worse than crashing.

N.b. fixed last code example- thanks afiori.

Re: Three new utility functions in C++23

#39
post #35

Earlier quoted context omitted.

If a code path that's supposed to be unreachable is reached then the program is already broken. Unless it has a bug, a compiler will not make a program more broken. At worst (or best, depending on how you look at it) it will only make any bugs it already has more obvious.

> At worst (or best, depending on how you look at it) it will only make any bugs it already has more obvious. An abort would make them obvious, surely unreachable makes them less obvious?

The point is that you're telling the compiler "I don't care what happens if control reaches here. Assume it never will and use that information to better optimize the rest". It's not an alternative to abort() or throwing because the compiler still needs to generate code for them.

Re: Three new utility functions in C++23

#40

Earlier quoted context omitted.

If you have undefined behavior, your program is already broken. No such thing as "more broken"; there's already no theoretical limit to what might happen if it gets triggered.

Undefined behavior is considered worse than crashing, which is typically the alternative when reaching "unreachable" codepaths. Compare these two blocks similar to the article switch (ch) { case 'a': do_a(); return; case 'd': do_d(); return; // ch is guaranteed to be 'a' or 'd' by previous code. default: assert(0); } switch (ch) { case 'a': do_a(); return; case 'd': do_d(); return; default: std::unreachable(); } If t…

I suspect you meant to omit the `if (c == 'd')` part in the last example
Post reply on HN