www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Setting up a final switch from a list

reply Salih Dincer <salihdb hotmail.com> writes:
On Wednesday, 2 August 2023 at 18:19:55 UTC, Cecil Ward wrote:
 Am I right in thinking that final switch can be used to check 
 that all the elements in an enum are handled in cases?
final switch is not considered safe with enums because it can take interpolated values, resulting in a runtime error: ```d import std.stdio : writeln; enum alphabet { a, b, c } string lookUp(alphabet value) {    with(alphabet) final switch(value)    {        case a: return "letter a"; break;        case b: return "letter b"; break;        case c: return "letter c"; break;    } } void main() {    lookUp(cast(alphabet)0).writeln; // okay     alphabet.b.lookUp.writeln; // okay    auto abc = alphabet.c;    abc.lookUp.writeln; // okay        auto abd = cast(alphabet)3;    //abd.lookUp.writeln; // runtime-error:    /* core.exception.SwitchError onlineapp.d(7): No appropriate switch clause found//*/ } ``` SDB 79
Aug 07 2023
next sibling parent reply Dom DiSc <dominikus scherkl.de> writes:
On Monday, 7 August 2023 at 17:08:56 UTC, Salih Dincer wrote:
 On Wednesday, 2 August 2023 at 18:19:55 UTC, Cecil Ward wrote:
 Am I right in thinking that final switch can be used to check 
 that all the elements in an enum are handled in cases?
final switch is not considered safe with enums because it can take interpolated values, resulting in a runtime error: ```d enum alphabet { a, b, c } auto abd = cast(alphabet)3; ```
If anything, then this cast is not safe. Also arithmetic operations on enums shall never result in enums, but instead will be implicitly cast to integers before the operation. And implicit cast from int to enums don't exist. So in safe code you have to do this cast explicitly and trust it explicitly - and you should not be surprised if trusted code does something bad. That's why you should be extra careful before you trust a cast (or other unsafe operation).
Aug 07 2023
parent reply Nick Treleaven <nick geany.org> writes:
On Monday, 7 August 2023 at 17:25:05 UTC, Dom DiSc wrote:
 If anything, then this cast is not  safe.
I think that casting int to enum is actually memory safe, though it is not type safe.
 Also arithmetic operations on enums shall never result in 
 enums, but instead will be implicitly cast to integers before 
 the operation.
Unfortunately this is not the case for all operations on enums: ```d auto e = alphabet.a; e |= alphabet.c; // allowed ``` I think bit shifts are allowed too. Due to these, final swift has to always generate a default case. Walter's sum types proposal would provide a type safe enum.
Aug 08 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/8/23 4:28 AM, Nick Treleaven wrote:
 On Monday, 7 August 2023 at 17:25:05 UTC, Dom DiSc wrote:
 If anything, then this cast is not  safe.
I think that casting int to enum is actually memory safe, though it is not type safe.
Yes, casting an enum is safe.
 Also arithmetic operations on enums shall never result in enums, but 
 instead will be implicitly cast to integers before the operation.
Unfortunately this is not the case for all operations on enums: ```d auto e = alphabet.a; e |= alphabet.c; // allowed ``` I think bit shifts are allowed too. Due to these, final swift has to always generate a default case.
If you read the integer promotion spec, if both operands are the same enum, the result is the same enum. https://dlang.org/spec/type.html#usual-arithmetic-conversions (scroll down to the enum part) -Steve
Aug 08 2023
parent reply Nick Treleaven <nick geany.org> writes:
On Tuesday, 8 August 2023 at 14:06:50 UTC, Steven Schveighoffer 
wrote:
 If you read the integer promotion spec, if both operands are 
 the same enum, the result is the same enum.

 https://dlang.org/spec/type.html#usual-arithmetic-conversions

 (scroll down to the enum part)
OK, thanks. ```d enum E { a, b, c } void f() { E e = E.a; e |= E.c; // same enum, so allowed per spec e = e >> 3; // RHS should be int, which shouldn't convert to E } ``` The spec says:
 If one operand is an enum and the other is the base type of 
 that enum, the result is the base type.
So `e >> 3` should be `int` and the last line of `f` should not compile. But it does. Is the spec wrong or the compiler?
Aug 08 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/8/23 12:34 PM, Nick Treleaven wrote:
 ```d
 enum E { a, b, c }
 
 void f()
 {
      E e = E.a;
      e |= E.c; // same enum, so allowed per spec
      e = e >> 3; // RHS should be int, which shouldn't convert to E
 }
 ```
 The spec says:
 
 If one operand is an enum and the other is the base type of that enum, 
 the result is the base type.
So `e >> 3` should be `int` and the last line of `f` should not compile. But it does. Is the spec wrong or the compiler?
It appears that bit shifting doesn't count: ```d auto x = uint.max << 1L; static assert(is(typeof(x)) == uint)); ``` https://dlang.org/spec/expression.html#shift_expressions
 The operands must be integral types, and undergo the Integer 
Promotions. The result type is the type of the left operand after the promotions. That seems incorrect given the test above. This is a quite old part of the compiler, and a lot of the spec is written to describe the behavior of the compiler. -Steve
Aug 08 2023
parent Nick Treleaven <nick geany.org> writes:
On Tuesday, 8 August 2023 at 18:29:42 UTC, Steven Schveighoffer 
wrote:
 If one operand is an enum and the other is the base type of 
 that enum, the result is the base type.
So `e >> 3` should be `int` and the last line of `f` should not compile. But it does. Is the spec wrong or the compiler?
It appears that bit shifting doesn't count: ```d auto x = uint.max << 1L; static assert(is(typeof(x)) == uint)); ``` https://dlang.org/spec/expression.html#shift_expressions
Ah, I see.
 The operands must be integral types, and undergo the Integer
Promotions. The result type is the type of the left operand after the promotions. That seems incorrect given the test above.
Yes, looks like the result is the left operand type without promotions.
Aug 08 2023
prev sibling next sibling parent kdevel <kdevel vogtner.de> writes:
On Monday, 7 August 2023 at 17:08:56 UTC, Salih Dincer wrote:
 final switch is not considered safe with enums because it can 
 take interpolated values, resulting in a runtime error:
Wasn't Cecil concerned with how to ensure at compile time that for all values of an enum there is a handler? I'll post my unsent draft from before the restore here: On Wednesday, 2 August 2023 at 18:19:55 UTC, Cecil Ward wrote:
 Am I right in thinking that final switch can be used to check 
 that all the elements in an enum are handled in cases?
According to [1] this is the case if and only if the switch expression is of that enum type. ``` // compiles with dmd -version=X // missing case qmark if compiled without version import std.stdio; import std.conv; import std.exception; enum terminators_t { percent = '%', colon = ':', qmark = '?' }; int main_ (string [] args) { enforce (args.length == 2, "need one arg"); enforce (args [1].length == 1, "arg must be one character"); auto input = args [1][0].to!terminators_t; final switch (input) { case terminators_t.percent: "PERCENT".writeln; break; case terminators_t.colon: "COLON".writeln; break; version (X) { case terminators_t.qmark: "QUESTION MARK".writeln; break; } } return 0; } int main (string [] args) { try return main_ (args); catch (Exception e) { stderr.writeln (e.msg); return 1; } } ``` But I like it without indentation, switch and break: ``` import std.stdio; import std.exception; void percent () { "PERCENT".writeln; } void colon () { "COLON".writeln; } void qmark () { "QUESTION MARK".writeln; } enum terminatorhandlers = [ // this is implicitly "final" '%' : &percent, ':' : &colon, '?' : &qmark, ]; int main_ (string [] args) { enforce (args.length == 2, "need one arg"); enforce (args [1].length == 1, "arg must be one character"); auto input = args [1] [0]; auto p = input in terminatorhandlers; // switch emulation enforce (p, "unknown terminator " ~ input); (*p) (); // call the registered action return 0; } int main (string [] args) { try return main_ (args); catch (Exception e) { stderr.writeln (e.msg); return 1; } } ``` [1] https://dlang.org/spec/statement.html#final-switch-statement
Aug 09 2023
prev sibling parent reply =?UTF-8?Q?Christian_K=c3=b6stlin?= <christian.koestlin gmail.com> writes:
I think one part of the original question (now fanished in the nntp 
backup) was how to get all enum members into a list without duplicating 
code.


```d
import std.traits : EnumMembers;
import std.stdio : writeln;
import std.algorithm : map;
import std.conv : to;
enum alphabet {
   a, b, c, d
}

void main()
{
   writeln(EnumMembers!alphabet);
   writeln([EnumMembers!alphabet]);
   writeln([EnumMembers!alphabet].map!(a => "test" ~a.to!string));
}
```

results in

```
abcd
[a, b, c, d]
["testa", "testb", "testc", "testd"]```
```
Aug 10 2023
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 10 August 2023 at 08:33:13 UTC, Christian Köstlin 
wrote:
 I think one part of the original question (now fanished in the 
 nntp backup) was how to get all enum members into a list 
 without duplicating code.


 ```d
 import std.traits : EnumMembers;
 import std.stdio : writeln;
 import std.algorithm : map;
 import std.conv : to;
 enum alphabet {
   a, b, c, d
 }

 void main()
 {
   writeln(EnumMembers!alphabet);
   writeln([EnumMembers!alphabet]);
   writeln([EnumMembers!alphabet].map!(a => "test" 
 ~a.to!string));
 }
 ```

 results in

 ```d
 abcd
 [a, b, c, d]
 ["testa", "testb", "testc", "testd"]```
 ```
How can we add all members of an enum type to a list without duplicating code? I wonder if this is something we'll see soon as the D language feature? In the D programming language, this can be achieved using features provided by the language such as __traits and AliasSeq. For instance, the EnumMembers trait from the std.traits module returns all members of an enum type as a tuple. This tuple contains the enum members in sequence, allowing for iteration over them or conversion into a list. Finally, utilizing these language features to avoid code duplication and write cleaner, more maintainable code is a good practice. Sorry to resurrect this old thread, but what do you think; people living in 2024 ? SDB 79
Mar 28
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, March 28, 2024 4:21:03 PM MDT Salih Dincer via Digitalmars-d-
learn wrote:
 How can we add all members of an enum type to a list without
 duplicating code?
As the documentation for EnumMembers explains, you can use std.meta.NoDuplicates to strip out duplicates if you want to do something like generate a switch statement from the list of enum members. https://dlang.org/phobos/std_traits.html#EnumMembers - Jonathan M Davis
Mar 28
parent Salih Dincer <salihdb hotmail.com> writes:
On Friday, 29 March 2024 at 00:37:21 UTC, Jonathan M Davis wrote:
 On Thursday, March 28, 2024 4:21:03 PM MDT Salih Dincer via 
 Digitalmars-d- learn wrote:
 How can we add all members of an enum type to a list without 
 duplicating code?
As the documentation for EnumMembers explains, you can use std.meta.NoDuplicates to strip out duplicates if you want to do something like generate a switch statement from the list of enum members. https://dlang.org/phobos/std_traits.html#EnumMembers - Jonathan M Davis
I guess this falls into metaprogramming. Maybe we should expect this possibility from IDEs because I've seen something like this in VScode. All enum members were added automatically. SDB 79
Mar 29