Live data from Hacker News

Three new utility functions in C++23

mariusbancila.ro

61–70 of 196 posts

Re: Three new utility functions in C++23

#61

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.

One thing people are missing is that you may need to use this to satisfy conditions that the compiler might encounter to emit a warning (or error if equivalent of -Werror is enabled). If you have strict warnings on, but no way to tell the compiler that a location should not be reachable, you wind up in situations where you are doing something like `assert(!"not reached!")` and it won't be enabled in all build types.…

For that abort() is just fine. Being UB, unreachable is more about optimizations .

Re: Three new utility functions in C++23

#62

Earlier quoted context omitted.

You can compromise by using abort in debug builds and std::unreachable in release builds.

I love it when switching debug mode on changes the control flow graph of my program. Keeps life interesting.

well, that's already the case.

Re: Three new utility functions in C++23

#63
post #43

Silly question from someone who hasn’t written C++ in 20 years and only very vaguely remembers it - if a code path is unreachable, why have the path at all? Is a default on a switch required? Is lack thereof a compiler warning or something? It’s been a very long time; I only ever seem to recall that kind of “all paths must be handled” from functional languages.

Not C++ specific, but the example given - a default case that is not expected to ever be hit - is a "cover all bases" thing, to avoid undefined behaviour. Without the default case, what should happen? In the example given, it would just do nothing, but it would do so silently, which could lead to hours of developer time wasted trying to figure out why it doesn't do anything. But I can imagine other use cases that are…

I get that covering all bases is good, but am I reading right that this just tells the compiler not to generate that path? Seems like the opposite.

Wouldn't it be safer just to trigger a panic or something?

Re: Three new utility functions in C++23

#64
post #43

Silly question from someone who hasn’t written C++ in 20 years and only very vaguely remembers it - if a code path is unreachable, why have the path at all? Is a default on a switch required? Is lack thereof a compiler warning or something? It’s been a very long time; I only ever seem to recall that kind of “all paths must be handled” from functional languages.

There are a number of reasons.

Having an unreachable block communicates to other humans that you didn't forget the else case, it shouldn't exist. Code is about communicating to the next maintainer of the code.

Unreachable communicates to static analyzers, which can throw an error if it detects a code path that would reach this code, even though otherwise that code path is fine. Also static analysis will stop analysis at this point, and since static analysis often is running into the halting problem having a forced halt means some other heuristic elsewhere will get more time to run and so it can find bugs in a different code path that it wouldn't have analyzed before.

> Is a default on a switch required?

Many style guide do not allow a default case on a switch. If you don't have a default case and you add a new item static analysis will flag an error (compiler warning), thus ensuring you look at that section of code that you may not have known about. So unreachable is a way to mark a lot of not possible cases as ones you have thought about, without either skipping them or adding a default.

> I only ever seem to recall that kind of “all paths must be handled” from functional languages.

C++ doesn't require all paths be handled, but realistically as a programmer you want to handle all paths. Marking a path as not reachable is a useful way to handle impossible code paths.

Re: Three new utility functions in C++23

#65
post #45

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 sa…

1.) Functions are fine for this stuff. The compiler can be trusted for its ability to "inline" the language-level `__builtin_unreachable()` or equivalent at the relevant optimization levels. 2.) static_cast is shorter, if you know that the underlying type is int. But even if you know, you might not want to spell out int, to be more robust to code changes, which might involve a change of the underlying type of the cor…

> 2.) static_cast is shorter, if you know that the underlying type is int. > But even if you know, you might not want to spell out int, to be more robust to > code changes, which might involve a change of the underlying type of the > corresponding enum. >

> In generic context you might not even know the underlying type.

Thanks for pointing this out! I'm aware that the underlying type of an enum isn't always `int`. This is what I meant by saying the `static_cast` feels more expressive to me, i.e. reading the code, at least I'll now on sight what type is being used here. I'd probably have to 'let go' more to effectively use CPP, but this category of mechanism to cope with a generic/type system so complicated that I can't figure out the type of anything anymore. In the same vein, 'making code more robust to changes' feels like kicking the can down the road (to me). At some point you'll need to deal with the actual type ...

Re: Three new utility functions in C++23

#66
post #63

Earlier quoted context omitted.

Not C++ specific, but the example given - a default case that is not expected to ever be hit - is a "cover all bases" thing, to avoid undefined behaviour. Without the default case, what should happen? In the example given, it would just do nothing, but it would do so silently, which could lead to hours of developer time wasted trying to figure out why it doesn't do anything. But I can imagine other use cases that are…

I get that covering all bases is good, but am I reading right that this just tells the compiler not to generate that path? Seems like the opposite. Wouldn't it be safer just to trigger a panic or something?

> Wouldn't it be safer just to trigger a panic or something?

Tradeoffs. The panic can result in a lot of code generation which in turn makes the reachable paths slower. Sure we are talking nanoseconds, but this is C++, if performance isn't important you shouldn't be using C++.

Re: Three new utility functions in C++23

#67
post #41

Why on earth are these in C++23 and not C++11? There are a ton of things that should’ve been standardized over a decade ago but only show up in C++20 or later. `std::span ` is a huge one. The mind boggles.

Trying to get everything is why C++11 wasn't c++07. The committee spend several years trying to polish things instead of doing a new release. (the committee thought they weren't allowed to release anything before 06/07, otherwise some of what was in C++11 could have been in C++01 - or maybe c++03 but with more in it). Eventually you need to say stop right here, what is done is what we will release, what isn't done will have to wait.

Re: Three new utility functions in C++23

#68
post #47

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 sa…

1. It is a compiler defined function (`__builtin_unreachable()`), but the issue is that MSVC doesn't have it, so you need a different implementation per compiler [0]. Plus, if a new compiler shows up (besides MSVC/GCC/LLVM), you'd need to investigate what the correct way to express `__builtin_unreachable` is. From a compiler perspective, using a function makes the most sense, since that fits into the existing control…

> 3. `htonl` are not standardized, so they were not part of the C++ standard library.

I was questioning the motivation of this to facilitate network-byte-order issues as stated in the blog post. The proposal[1] (that the post linked to) also confirmed that the motivation was to expose more machine code intrinsics rather than deal with network byte order like I expected.

Concerning 1.) yeah, I guess I'd prefer a #pragma aesthetically, but didn't think about the fact that it wouldn't be exposed to the compiler.

Thanks for the well thought out reply!

[1] : https://isocpp.org/files/papers/P1272R4.html#motivation

Re: Three new utility functions in C++23

#70

Earlier quoted context omitted.

One thing people are missing is that you may need to use this to satisfy conditions that the compiler might encounter to emit a warning (or error if equivalent of -Werror is enabled). If you have strict warnings on, but no way to tell the compiler that a location should not be reachable, you wind up in situations where you are doing something like `assert(!"not reached!")` and it won't be enabled in all build types.…

For that abort() is just fine. Being UB, unreachable is more about optimizations .

>> and it won't be enabled in all build types.

The typical usecase would to wrap this in a #define, so that it aborts on a debug build but you get faster code in a production build.

Post reply on HN