Thread overview | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
April 21, 2020 Enum conversion | ||||
---|---|---|---|---|
| ||||
Attachments:
| Hi,
Given an enum:
enum ZoneNumber {
One = 1,
Two = 2,
}
then which of these is the right way of accessing the value?
cast(ubyte)ZoneNumber.One
to!ubyte(ZoneNumber.One)
conversely what is the right way of going the other way:
cast(ZoneNumber)1
to!ZoneNumber(1)
I tried:
enum ZoneNumber : ubyte {
One = 1,
Two = 2,
}
but the members One and Two still seem to be types as int. :-(
--
Russel.
===========================================
Dr Russel Winder t: +44 20 7585 2200
41 Buckmaster Road m: +44 7770 465 077
London SW11 1EN, UK w: www.russel.org.uk
|
April 21, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russel Winder | On 4/21/20 12:03 PM, Russel Winder wrote: > Hi, > > Given an enum: > > enum ZoneNumber { > One = 1, > Two = 2, > } > > then which of these is the right way of accessing the value? > > cast(ubyte)ZoneNumber.One > to!ubyte(ZoneNumber.One) I generally do this: ubyte(ZoneNumber.One) > > conversely what is the right way of going the other way: > > cast(ZoneNumber)1 This will incur zero runtime cost, so I would recommend that. > to!ZoneNumber(1) This works too, I think it just does the equivalent of the first, but if not inlined, you will incur some runtime cost. > > I tried: > > enum ZoneNumber : ubyte { > One = 1, > Two = 2, > } > > but the members One and Two still seem to be types as int. :-( They are typed as ZoneNumber, which is a derivative of ubyte. What measurement are you doing to determine that they are int? auto x = ZoneNumber.One; ubyte y = x; // fine If you leave off the :ubyte part, the declaration of y would fail. Similarly, this would fail: enum ZoneMember : ubyte { One = 1, Two = 2, ThreeThousand = 3000, // Error: cannot implicitly convert expression 3000 of type int to ubyte } -Steve |
April 21, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russel Winder | On Tuesday, 21 April 2020 at 16:03:20 UTC, Russel Winder wrote: > then which of these is the right way of accessing the value? > > cast(ubyte)ZoneNumber.One > to!ubyte(ZoneNumber.One) Either is acceptable because there is no way that this operation can fail. Using a naked `cast` makes less work for the compiler and performs optimally with or without inlining, though. > conversely what is the right way of going the other way: > > cast(ZoneNumber)1 > to!ZoneNumber(1) Use `to` except where you can gaurantee that the input value maps to a valid enum member, because `cast` does not check your work: writeln(cast(ZoneNumber)17); // breaks the type system writeln(to!ZoneNumber(17)); // throws a ConvException: Value (17) does not match any member value of enum 'ZoneNumber' So, `cast` is faster, but unsafe. `to` is slower, but protects the enum's invariant. |
April 21, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer Attachments:
| On Tue, 2020-04-21 at 12:59 -0400, Steven Schveighoffer via Digitalmars-d-learn wrote: > On 4/21/20 12:03 PM, Russel Winder wrote: > > Hi, > > > > Given an enum: > > > > enum ZoneNumber { > > One = 1, > > Two = 2, > > } > > > > then which of these is the right way of accessing the value? > > > > cast(ubyte)ZoneNumber.One > > to!ubyte(ZoneNumber.One) > > I generally do this: > > ubyte(ZoneNumber.One) Hummm… I hadn't got to that one. :-) Why choose that rather than one of the other two? > > > conversely what is the right way of going the other way: > > > > cast(ZoneNumber)1 > > This will incur zero runtime cost, so I would recommend that. > > > to!ZoneNumber(1) > > This works too, I think it just does the equivalent of the first, but > if > not inlined, you will incur some runtime cost. > > > I tried: > > > > enum ZoneNumber : ubyte { > > One = 1, > > Two = 2, > > } > > > > but the members One and Two still seem to be types as int. :-( > They are typed as ZoneNumber, which is a derivative of ubyte. What measurement are you doing to determine that they are int? typeof(ZoneNumber.One).stringof seemed to return uint. > > auto x = ZoneNumber.One; > ubyte y = x; // fine > > If you leave off the :ubyte part, the declaration of y would fail. > > Similarly, this would fail: > > enum ZoneMember : ubyte { > One = 1, > Two = 2, > ThreeThousand = 3000, // Error: cannot implicitly convert > expression 3000 of type int to ubyte > } Oooo… I like this, adding ":ubyte" immediately. -- Russel. =========================================== Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Road m: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk |
April 21, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to tsbockman Attachments:
| On Tue, 2020-04-21 at 18:09 +0000, tsbockman via Digitalmars-d-learn wrote: > On Tuesday, 21 April 2020 at 16:03:20 UTC, Russel Winder wrote: > > then which of these is the right way of accessing the value? > > > > cast(ubyte)ZoneNumber.One > > to!ubyte(ZoneNumber.One) > > Either is acceptable because there is no way that this operation can fail. Using a naked `cast` makes less work for the compiler and performs optimally with or without inlining, though. Seems like in this case cast is better than to! use. > > conversely what is the right way of going the other way: > > > > cast(ZoneNumber)1 > > to!ZoneNumber(1) > > Use `to` except where you can gaurantee that the input value maps to a valid enum member, because `cast` does not check your work: > > writeln(cast(ZoneNumber)17); // breaks the type system > writeln(to!ZoneNumber(17)); // throws a ConvException: Value > (17) does not match any member value of enum 'ZoneNumber' > > So, `cast` is faster, but unsafe. `to` is slower, but protects the enum's invariant. Thanks. It seems to! is de rigueur in this case. -- Russel. =========================================== Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Road m: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk |
April 21, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russel Winder | On 4/21/20 3:00 PM, Russel Winder wrote: > On Tue, 2020-04-21 at 12:59 -0400, Steven Schveighoffer via > Digitalmars-d-learn wrote: >> On 4/21/20 12:03 PM, Russel Winder wrote: >>> Hi, >>> >>> Given an enum: >>> >>> enum ZoneNumber { >>> One = 1, >>> Two = 2, >>> } >>> >>> then which of these is the right way of accessing the value? >>> >>> cast(ubyte)ZoneNumber.One >>> to!ubyte(ZoneNumber.One) >> >> I generally do this: >> >> ubyte(ZoneNumber.One) > > Hummm… I hadn't got to that one. :-) > > Why choose that rather than one of the other two? 1. it's shorter and prettier. 2. No cast (I avoid using cast whenever I can). 3. No gotcha type conversions. e.g. for point 3: enum ZoneMember { // : int One = 1, Two = 2, ReallyBig = 4567, } auto b1 = ubyte(ZoneNumber.One); // 1 (compiler uses VRP to make this work) auto b2 = ubyte(ZoneNumber.ReallyBig); // Compiler error vs. auto b1 = cast(ubyte)ZoneNumber.One; // 1 auto b2 = cast(ubyte)ZoneNumber.ReallyBig; // b2 == 215 (truncated) vs. auto b1 = to!ubyte(ZoneNumber.One); // 1 auto b2 = to!ubyte(ZoneNumber.ReallyBig); // runtime error > > >> >>> conversely what is the right way of going the other way: >>> >>> cast(ZoneNumber)1 >> >> This will incur zero runtime cost, so I would recommend that. >> >>> to!ZoneNumber(1) >> >> This works too, I think it just does the equivalent of the first, but >> if >> not inlined, you will incur some runtime cost. >> >>> I tried: >>> >>> enum ZoneNumber : ubyte { >>> One = 1, >>> Two = 2, >>> } >>> >>> but the members One and Two still seem to be types as int. :-( >> They are typed as ZoneNumber, which is a derivative of ubyte. What >> measurement are you doing to determine that they are int? > > typeof(ZoneNumber.One).stringof seemed to return uint. This is what happens for me: enum ZoneNumber : ubyte { One = 1, Two = 2, } pragma(msg, typeof(ZoneNumber.One).stringof); // ZoneNumber -Steve |
April 21, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to tsbockman | On 4/21/20 2:09 PM, tsbockman wrote:
>> conversely what is the right way of going the other way:
>>
>> cast(ZoneNumber)1
>> to!ZoneNumber(1)
>
> Use `to` except where you can gaurantee that the input value maps to a valid enum member, because `cast` does not check your work:
>
> writeln(cast(ZoneNumber)17); // breaks the type system
> writeln(to!ZoneNumber(17)); // throws a ConvException: Value (17) does not match any member value of enum 'ZoneNumber'
>
> So, `cast` is faster, but unsafe. `to` is slower, but protects the enum's invariant.
I just want to correct this and say there isn't a type system requirement for the enum to be only one of the selected values, even in safe code.
e.g.:
enum flags {
one = 1,
two = 2,
}
flags f = flags.one | flags.two; // ok
++f; // ok also
In essence, an enum acts as a derived type with named constants.
Also, there is one situation where you can't use to -- a string-based enum:
enum sym : string {
s = "s value",
y = "y value"
}
auto a = cast(sym)"s value"; // ok
assert(a == sym.s);
auto b = to!sym("s value"); // runtime error
This is because to!someEnum(string) is specialized to look at the enum names only, not the values.
-Steve
|
April 22, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer Attachments:
| On Tue, 2020-04-21 at 15:48 -0400, Steven Schveighoffer via Digitalmars-d-learn wrote: […] > > 1. it's shorter and prettier. > 2. No cast (I avoid using cast whenever I can). > 3. No gotcha type conversions. Works for me, you have me convinced. :-) > e.g. for point 3: > > enum ZoneMember { // : int > One = 1, > Two = 2, > ReallyBig = 4567, > } > > auto b1 = ubyte(ZoneNumber.One); // 1 (compiler uses VRP to make this > work) > auto b2 = ubyte(ZoneNumber.ReallyBig); // Compiler error > > vs. > > auto b1 = cast(ubyte)ZoneNumber.One; // 1 > auto b2 = cast(ubyte)ZoneNumber.ReallyBig; // b2 == 215 (truncated) > > vs. > > auto b1 = to!ubyte(ZoneNumber.One); // 1 > auto b2 = to!ubyte(ZoneNumber.ReallyBig); // runtime error QED. Though for converting a ulong to a ubyte, I am assuming to!ubyte(x) is the right tool for the job. […] > pragma(msg, typeof(ZoneNumber.One).stringof); // ZoneNumber Which is as it should be really. I was clearly having a mental aberration. Anyway, the probem is now solved! :-) Thanks for your input, much appreciated. -- Russel. =========================================== Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Road m: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk |
April 22, 2020 Re: Enum conversion | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russel Winder | On 4/22/20 6:36 AM, Russel Winder wrote:
> Though for converting a ulong to a ubyte, I am assuming to!ubyte(x) is
> the right tool for the job.
It depends! If you know that the long will fit in a ubyte, by all means just use a cast. It's the fastest option. If you have no idea the value of the long, but it's *supposed* to fit into a ubyte, use to if you want an exception for those outside the range. And if you don't care, and just want a ubyte, use a cast.
But the compiler isn't going to accept ubyte(someLong).
-Steve
|
Copyright © 1999-2021 by the D Language Foundation