Please don’t use std::unreachable in switch statements like this, it breaks the compiler warning that you’ve missed a case. It’s also probably a good idea never to use it directly and instead use it in a debug function that checks the thing that you believe is impossible in debug builds (and builds that you’re fuzzing) and only allows the compiler to optimise based on it in release builds. Also, be aware that the compiler can’t dead-code-eliminate functions leading to an unreachable call unless it can prove that they don’t have side effects.
std::to_underlying looks useful for simplifying code that uses enum classs as unit types. It’s weirdly asymmetric though. I guess a std::from_underlying would be harder to use correctly since it may provide an invalid enum value and there’s no standard way of enumerating valid values to check.
std::byteswap has been a long time coming, but it is a weird API. The bits header contains an endian enumeration and I’d expect a less ambiguous API to be of the form to_byte_order<endian> and from_byte_order<endian>. You can implement these as:
template<typename T, std::endian E>
constexpr T to_byte_order(T value) requires std::integral<T>
{
if constexpr (E == std::endian::native)
{
return value;
}
return std::byteswap(value);
}
Also, be aware that the compiler can’t dead-code-eliminate functions leading to an unreachable call unless it can prove that they don’t have side effects.
Isn’t the point that reaching std::unreachable is undefined behaviour, so the compiler is allowed to assume that the branch which executes it doesn’t have any side effects?
Edit: I would advocate abort() rather than unreachable() though. It costs very little code size and is much easier to debug.
Anything on the path to unreachable and nowhere else is subject to DCE, but I’ve seen a fairly common misuse of __builtin_unreachable that looks something like this:
if (someFn())
__builtin_unreachable();
If you write this, the compiler doesn’t just assume that the function will return false and skip the call, it executes the function and eliminates the conditional. This kind of use is fine if someFn can be inlined and is side-effect free. If it has side effects (or can’t be proven not to have side effects) then it will still be generated.
This kind of thing is why __builtin_assume is generally better than __builtin_unreachable as a hint to the compiler. Unfortunately, there isn’t yet a standard way of expressing that in C++.
Huh, I thought you needed an explicit cast for that. I am constantly surprised by things that the C++ type system lets me do without warnings or errors.
I think this got added in C++17. The nice thing is that it’s a bit safer than Enum(i) because it requires that the type of i conforms to the enum’s underlying type; so you’d get a compile error if Enum is based on int but i is an int64_t, for example.
I guess it’s fine as long as it’s an implicit cast. In C, enum is basically useless because you can implicitly cast from int to enum foo and so you must assume that any enum variable may hold invalid values and defensively check. In C++, enum class is a lot more robust against that (and, unlike Rust, the compiler will not ‘helpfully’ delete any defensive checks that you do add if you are in code reading an enum from a memory-mapped I/O device or similar).
byteswap seems less than pointless. It works on ints, but byteswapping is only a meaningful operation on byte arrays. It assumes that you’re going to cast your int into a byte array, but that’s a system defined operation. The API you would actually want is given an int and a pointer to a byte array (and maybe a precision), fill up or read from the byte array in the right direction, so that you can write a portable and performant flip. This instead is not portable, so you might as well just use the right intrinsics from the start!
It assumes that you’re going to cast your int into a byte array, but that’s a system defined operation.
Yes, but it’s just as architecture dependent as the implementation of byteswap, and they are by definition consistent with each other.
To my knowledge, the CPU instructions that do byteswapping operate on numbers already in registers, rather than applying to a load/store. So the byteswap function has similar semantics. This might improve code gen?
Looks as if the link is working now.
Please don’t use
std::unreachable
in switch statements like this, it breaks the compiler warning that you’ve missed a case. It’s also probably a good idea never to use it directly and instead use it in a debug function that checks the thing that you believe is impossible in debug builds (and builds that you’re fuzzing) and only allows the compiler to optimise based on it in release builds. Also, be aware that the compiler can’t dead-code-eliminate functions leading to an unreachable call unless it can prove that they don’t have side effects.std::to_underlying
looks useful for simplifying code that usesenum class
s as unit types. It’s weirdly asymmetric though. I guess astd::from_underlying
would be harder to use correctly since it may provide an invalidenum
value and there’s no standard way of enumerating valid values to check.std::byteswap
has been a long time coming, but it is a weird API. Thebits
header contains anendian
enumeration and I’d expect a less ambiguous API to be of the formto_byte_order<endian>
andfrom_byte_order<endian>
. You can implement these as:But I’m not sure why this wasn’t the default API.
Isn’t the point that reaching std::unreachable is undefined behaviour, so the compiler is allowed to assume that the branch which executes it doesn’t have any side effects?
Edit: I would advocate abort() rather than unreachable() though. It costs very little code size and is much easier to debug.
Sorry, I should have been clearer:
Anything on the path to unreachable and nowhere else is subject to DCE, but I’ve seen a fairly common misuse of
__builtin_unreachable
that looks something like this:If you write this, the compiler doesn’t just assume that the function will return false and skip the call, it executes the function and eliminates the conditional. This kind of use is fine if
someFn
can be inlined and is side-effect free. If it has side effects (or can’t be proven not to have side effects) then it will still be generated.This kind of thing is why
__builtin_assume
is generally better than__builtin_unreachable
as a hint to the compiler. Unfortunately, there isn’t yet a standard way of expressing that in C++.std::from_underlying<Enum>(i)
would be the same asEnum{i}
, wouldn’t it? Which is shorter.Huh, I thought you needed an explicit cast for that. I am constantly surprised by things that the C++ type system lets me do without warnings or errors.
I think this got added in C++17. The nice thing is that it’s a bit safer than
Enum(i)
because it requires that the type ofi
conforms to the enum’s underlying type; so you’d get a compile error ifEnum
is based onint
buti
is anint64_t
, for example.I guess it’s fine as long as it’s an implicit cast. In C,
enum
is basically useless because you can implicitly cast fromint
toenum foo
and so you must assume that any enum variable may hold invalid values and defensively check. In C++,enum class
is a lot more robust against that (and, unlike Rust, the compiler will not ‘helpfully’ delete any defensive checks that you do add if you are in code reading an enum from a memory-mapped I/O device or similar).byteswap seems less than pointless. It works on ints, but byteswapping is only a meaningful operation on byte arrays. It assumes that you’re going to cast your int into a byte array, but that’s a system defined operation. The API you would actually want is given an int and a pointer to a byte array (and maybe a precision), fill up or read from the byte array in the right direction, so that you can write a portable and performant flip. This instead is not portable, so you might as well just use the right intrinsics from the start!
Yes, but it’s just as architecture dependent as the implementation of byteswap, and they are by definition consistent with each other.
To my knowledge, the CPU instructions that do byteswapping operate on numbers already in registers, rather than applying to a load/store. So the byteswap function has similar semantics. This might improve code gen?