Live data from Hacker News

Enum class improvements for C++17, C++20 and C++23

cppstories.com

81–90 of 121 posts

Re: Enum class improvements for C++17, C++20 and C++23

#81

Earlier quoted context omitted.

That's by design. Consider mapping a file or reinterpret casting a network buffer that contains a structure with such an enum: if has been written by a different version of an application, the possible enum values might be different. That was considered, among other thing, an important use case to support. You can easily build your own safe enum on top of you really want. Edit: someone else pointed out the bitmask us…

I don't think reinterpret casting enums is something that should be encouraged and uncovering such points where unexpected values can be introduced is exactly the point of having a strict enum. Just because putting random values into the enum isn't itself undefined behavior doesn't mean that your program isn't going to blow up later on. Bitmask enums are also a hack and the better solution is to have different types…

Having tried the solution with a separate type for the bitset as opposed to the enum itself, in my experience the complexity it is just not worth the improvement.

> but when enum class was designed there were no existing usages for that.

IIRC diverging from standard enum was considred, but rejected as deemed a surprising change in behaviour.

Re: Enum class improvements for C++17, C++20 and C++23

#82

All that crap is the reason I never bother with enums in all these C like languages set_player_color(player, .RED); That should be the way to use them, it's concise and typesafe set_color(Color::RED); Why repeat yourself? It's one of the things I love about Swift and Zig int main() { using enum ComputeStatus; ComputeStatus s = NotEnoughMemory; } now you have polluted the scope.. C++ have lost the plot.. they understo…

It's obvious with Color, but when you deal with more complex stuff, code like computation_service.set_status(ComputeStatus::NotEnoughMemory); system.set_state(SystemState::Failure); is a lot clearer than computation_service.set_state(.NotEnoughMemory); system.set_state(.Failure); in my opinion. Both objects/structs/whatevers can have a state type of their own but the shorthand form does not indicate whether or not th…

Obviously, you should still have the choice to be verbose when the context requires it, pragmatism is the key

In your example, to me, the value is obfuscated, I prefer conciseness, it's easier to read and it makes more sense

Besides, it encourages developers to better name their variables/functions

SwiftUI made me appreciate it more

Re: Enum class improvements for C++17, C++20 and C++23

#83
post #9

enum class Handle : uint32_t { Invalid = 0 }; Handle h { 42 }; // OK One of their examples demonstrates the number one issue for me with enums, which was not fixed with `enum class`. Since values outside the range of the type are valid, you are constantly needing to check for invalid values in any function that takes an enum [class]. Ruins any attempt at "parse, don't validate" style in c++ and completely ruins the "…

What the alternative? Let’s say you have a file, you parse a uint32_t, and you want to convert that into the Handle type. If the enum is closed how do you do it? Giant switch? That would break the fundamental principle that C++ abstractions are zero-cost.

You either need that giant switch or checks at every use anyway in order to protect yourself against bad data. Better yet would be an explicit mapping of numbers in the file and enum values so that your file format and runtime enum are decoupled. The compiler can still optimize that into a simple range check when the values match up.

Re: Enum class improvements for C++17, C++20 and C++23

#84
post #38

Earlier quoted context omitted.

The main difference between old ("unscoped") and new ("scoped") enums, besides dropping implicit conversions from/to integral types, is the scope of the named constants. With unscoped enums, the constants are in the surrounding scope, which means that constants with the same name but of different enum types collide with each other. One solution is to wrap them in a dummy struct or class (`struct Foo { enum Bar { baz…

> The reason other values of the underlying integral type beyond the named ones are allowed, is that enums are often used to specify bit values or bit masks that you AND/OR with each other. It is what it is. Except bitwise operations of orign enum values yields the underlying type and not an enum value. And for enum classes the operators don't exist at all. So you need to write custom operators(or manual casts) for t…

> Except orign enum values yields the underlying type and not an enum value

False:

    enum Foo { foo, bar };
    auto x  = foo|bar;
    static_assert(std::is_same_v);
edit: I'm wrong, see below.

Re: Enum class improvements for C++17, C++20 and C++23

#85
post #56
post #16

Earlier quoted context omitted.

Well, apparently it was fixed with `enum class`… until it was un fixed in C++17 for unfathomable reasons. It’s honestly crazy that C++ doesn’t have simple exhaustiveness-checked enums. The obvious actual solution for the valid use case of deserializing an enum from an integer would be something like template std::optional from_underlying(std::underlying_type ) requires(std::is_enum_v )

It wasn't unfixed in C++17. The problem existed before C++17. This is valid code in C++11: enum class Foo : int { Invalid = 0 }; Foo f = Foo(5); In C++11, this compiles with Foo(5) but not Foo{5}. In C++17, this compiles with both Foo(5) and Foo{5}.

Uf, I see, thanks. I could’ve sworn that this was one of the things enum class was supposed to fix.

Re: Enum class improvements for C++17, C++20 and C++23

#86

Union, inheritance, dynamic_cast, enum class, std::variant... when all we needed all along was proper sum types.

My guess currently is that C++ 29 will get some sort of pattern matching. It's too big a feature to board the C++ 26 train in autumn 2024 and there's nowhere close to consensus on how it should work or how it's spelled yet. So, assume C++ 29 has pattern matching. That causes people to attempt various idiomatic constructions from languages which always had pattern matching, and of course they're all rather awkward in…

There are two competing pattern matching proposals, hopefully they'll converge soon.

Re: Enum class improvements for C++17, C++20 and C++23

#87
post #9

enum class Handle : uint32_t { Invalid = 0 }; Handle h { 42 }; // OK One of their examples demonstrates the number one issue for me with enums, which was not fixed with `enum class`. Since values outside the range of the type are valid, you are constantly needing to check for invalid values in any function that takes an enum [class]. Ruins any attempt at "parse, don't validate" style in c++ and completely ruins the "…

Why is an enum a class? What does that even mean semantically? Why can't an enum simply be ... an enum?

Even if under the covers an enum is/was implemented as a special class, why would the language syntax leak this implementation detail to the programmer?

Perhaps it would be cleaner to make enum its own concept, rather than trying to shoehorn it into classes. It is a fundamentally different thing than a class after all. Is there a reason, why this is not done?

Re: Enum class improvements for C++17, C++20 and C++23

#88
post #74

Earlier quoted context omitted.

That's by design. Consider mapping a file or reinterpret casting a network buffer that contains a structure with such an enum: if has been written by a different version of an application, the possible enum values might be different. That was considered, among other thing, an important use case to support. You can easily build your own safe enum on top of you really want. Edit: someone else pointed out the bitmask us…

You can justify the escape hatches for every feature in C++. The problem is that we eschew sensible defaults to handle the edge cases. Without going into a rust war, I think rust's unsafe is a great way to handle this - for 99% of use cases, you _really_ don't want to put an invalid enum value in there. But, in the number of cases where you do, you should have an escape hatch to do so. If you could do: my_enum_type f…

I assure you I'm very critical of C++ bad defaults. But in this case I think it was the right solution. There were three options:

1. make invalid values UB.

2. make invalid values non-representable by enforcing checks.

3. enum class is just a strong integer typedef with named constants.

Luckily 1 was reject: already too much UB. 2 would require runtime checking and was not considred viable by many; also it would prevent a lot of useful cases. 3 was the remaining option and was consistent with existing enum usage.

Re: Enum class improvements for C++17, C++20 and C++23

#89

Earlier quoted context omitted.

> The reason other values of the underlying integral type beyond the named ones are allowed, is that enums are often used to specify bit values or bit masks that you AND/OR with each other. It is what it is. Except bitwise operations of orign enum values yields the underlying type and not an enum value. And for enum classes the operators don't exist at all. So you need to write custom operators(or manual casts) for t…

> Except orign enum values yields the underlying type and not an enum value False: enum Foo { foo, bar }; auto x = foo|bar; static_assert(std::is_same_v ); edit: I'm wrong, see below.

I meant bitwise operations of enum values and not the values themselves of course. The type of x in your example is int.

Re: Enum class improvements for C++17, C++20 and C++23

#90

enum class Handle : uint32_t { Invalid = 0 }; // process({10}); // error process(Handle{10}); > In C++14, you could use process(static_cast (10)); so, as you can see, the C++17 version is much better. 100% disagree that the c++17 version is better. It just lets you right broken code that doesn't look broken.

Agreed, this isn't something that should be encouraged with a nicer syntax.
Post reply on HN