Live data from Hacker News

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

cppstories.com

91–100 of 121 posts

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

#91
post #32
post #30

Earlier quoted context omitted.

Yes, you read about std::variant on a blog and think that it is a sum type. Then you try it out and realize that it's a thin (type-safe) wrapper over tagged unions that is at least three times slower and has about 5 unreadable alternatives that replace simple switch statements. Then you find out that members of a "variant" are not really variant members but just the individual types that can be assigned to a union. F…

A few code snippets of what you see as weaknesses of std::variant may be appropriate, as I couldn't figure out your complaint. Assigning to a variant taken by non-const& works fine for me. I personally would have liked to see recursive variant types and multi-visitation (as supported by boost::variant).

std::variant is not a true algebraic data type, since the individual element constructors do not construct the variant type automatically. Compare to OCaml, written in a verbose and unidiomatic way that is similar to C++:

  # type foo = Int of { n : int } | Float of { f : float };;
  type foo = Int of { n : int; } | Float of { f : float; }
  # Int { n = 10 };;
  - : foo = Int {n = 10}
  # let r = ref (Int { n = 10 });;
  val r : foo ref = {contents = Int {n = 10}}
Notice that the constructor Int { n = 10 } automatically produces a foo type and assigning to a mutable ref works.

The same in C++, using assignment to a pointer to avoid the lvalue ref error that is irrelevant to this discussion:

  #include 
  
  struct myint {
    int n;
    myint(int n) : n(n) {}
  };

  struct myfloat {
    float f;
    myfloat(float f) : f(f) {}
  };

  using foo = std::variant;

  int
  main()  
  {
    const foo& x = myint{10}; // works
    foo *z = new myint{10}; // error: cannot convert ‘myint*’ to ‘foo*
  }

As stated above, this obviously cannot work since C++ has no way of specifying a myint constructor that -- like in OCaml -- automatically produces the variant type foo.

C++ would need true algebraic data types with compiler support (that would hopefully be as fast as switch statements). To be useful, they would need a nice syntax and not some hypothetical abomination like:

  using foo = std::variant where
  struct myint of foo { ... };

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

#92
post #74

Earlier quoted context omitted.

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

No, there were 4. Enforce runtime checking by default, and allow for casting _into_ a value to elide the checks. Think operator[] vs .at

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

#93

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…

> now you have polluted the scope Actually they haven't. Using glob imports inside small functions can enhance readability without causing confusion. Otherwise any kind of aliasing and importing would be polluting the scope since they are bringing other items into your module without the full path.

Putting using statements inside small functions is the opposite of ehancing readbility.

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

#94

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…

I disagree about which one of your examples is clearer. The first has too much visual noise you need to parse before you can see what is going on.

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

#95

Earlier quoted context omitted.

they easily can :) void test(int& y){} int main() { int* x = nullptr; test(*x); }

And you can also open /proc/self/mem in a Rust program and overwrite whatever you want, including pointers. So?

One of those cases happens accidentally all the time (in more complex variants than the motivating example you responded to), the other never happens except on purpose. It's like complaining guard rails are pointless because people being launched with catapults might still fly over them and plunge to their deaths.

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

#96

Earlier quoted context omitted.

> 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.

Ah! I meant to write decltype(x). And indeed you are right, it is int. TIL. I had checked on godblot, but of course the typo misled me.

In retrospect it makes sense, there is no operator| for enum, it is just calling the int version after implicit conversion.

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

#97
post #92

Earlier quoted context omitted.

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

No, there were 4. Enforce runtime checking by default, and allow for casting _into_ a value to elide the checks. Think operator[] vs .at

That would be just 2 right? This is c++, you would always be able to memcpy into it. But you have to deal with UB.

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

#98
post #35

Earlier quoted context omitted.

Inheritance (the subtyping part of it) is considered the OOP way to write sum type. sum Expr { Int; Add(Int,Int) } VS class Expr { } class Add extends Expr { Expr left; Expr right; }

You should probably look up what a sum type is, it has nothing to do with summations. Your example doesn't contain a sum type. A C++ example: std:variant sum_type_instance = 5;

My sum type example is exactly this (but I didn't use C++ std::variant syntax to not confuse the reader).

The most common example of a sum type is the "Expression problem" - please read some literature before commenting on a topic.

(Btw, it's called sum type for a reason: summation. The cardinality of the sum type is the sum of the cardinality of its variants)

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

#99
post #35

Earlier quoted context omitted.

Inheritance (the subtyping part of it) is considered the OOP way to write sum type. sum Expr { Int; Add(Int,Int) } VS class Expr { } class Add extends Expr { Expr left; Expr right; }

That's not C++.

Any reader who comment here hopefully has enough knowledge to understand the implied C++.

struct Add {} std::variant Expr;

OR

class Expr {} class Add : Expr {}

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

#100
post #20

Earlier quoted context omitted.

an event loop often wants an event type enum that has defined values for internal events and then everything out of range means pass onto the users handler. There are other variations where you need to pass an enum value without caring what it mean.

This isn't rocket science once you have sum types. enum Event { System(SystemEvent), User(T), }

At least in C++ a template needs to be known at compile time, but I want to build my event loop and latter add in more values that should be handled without rebuilding it.
Post reply on HN