www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Enum conversion

reply Russel Winder <russel winder.org.uk> writes:
Hi,

Given an enum:

enum ZoneNumber {
    One =3D 1,
    Two =3D 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 =3D 1,
    Two =3D 2,
}

but the members One and Two still seem to be types as int. :-(


--=20
Russel.
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
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
Apr 21 2020
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Apr 21 2020
parent reply Russel Winder <russel winder.org.uk> writes:
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,
=20
 Given an enum:
=20
 enum ZoneNumber {
      One =3D 1,
      Two =3D 2,
 }
=20
 then which of these is the right way of accessing the value?
=20
 cast(ubyte)ZoneNumber.One
 to!ubyte(ZoneNumber.One)
=20 I generally do this: =20 ubyte(ZoneNumber.One)
Hummm=E2=80=A6 I hadn't got to that one. :-) Why choose that rather than one of the other two?
=20
 conversely what is the right way of going the other way:
=20
 cast(ZoneNumber)1
=20 This will incur zero runtime cost, so I would recommend that. =20
 to!ZoneNumber(1)
=20 This works too, I think it just does the equivalent of the first, but if=20 not inlined, you will incur some runtime cost. =20
 I tried:
=20
 enum ZoneNumber : ubyte {
      One =3D 1,
      Two =3D 2,
 }
=20
 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=20 measurement are you doing to determine that they are int?
typeof(ZoneNumber.One).stringof seemed to return uint.
=20
 auto x =3D ZoneNumber.One;
 ubyte y =3D x; // fine
=20
 If you leave off the :ubyte part, the declaration of y would fail.
=20
 Similarly, this would fail:
=20
 enum ZoneMember : ubyte {
     One =3D 1,
     Two =3D 2,
     ThreeThousand =3D 3000, //  Error: cannot implicitly convert=20
 expression 3000 of type int to ubyte
 }
Oooo=E2=80=A6 I like this, adding ":ubyte" immediately. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D 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
Apr 21 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Apr 21 2020
parent reply Russel Winder <russel winder.org.uk> writes:
On Tue, 2020-04-21 at 15:48 -0400, Steven Schveighoffer via
Digitalmars-d-learn wrote:
[=E2=80=A6]
=20
 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:
=20
 enum ZoneMember { // : int
    One =3D 1,
    Two =3D 2,
    ReallyBig =3D 4567,
 }
=20
 auto b1 =3D ubyte(ZoneNumber.One); // 1 (compiler uses VRP to make this
 work)
 auto b2 =3D ubyte(ZoneNumber.ReallyBig); // Compiler error
=20
 vs.
=20
 auto b1 =3D cast(ubyte)ZoneNumber.One; // 1
 auto b2 =3D cast(ubyte)ZoneNumber.ReallyBig; // b2 =3D=3D 215 (truncated)
=20
 vs.
=20
 auto b1 =3D to!ubyte(ZoneNumber.One); // 1
 auto b2 =3D 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. [=E2=80=A6]
 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. =20 --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D 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
Apr 22 2020
parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Apr 22 2020
prev sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
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.
Apr 21 2020
next sibling parent Russel Winder <russel winder.org.uk> writes:
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?
=20
 cast(ubyte)ZoneNumber.One
 to!ubyte(ZoneNumber.One)
=20 Either is acceptable because there is no way that this operation=20 can fail. Using a naked `cast` makes less work for the compiler=20 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:
=20
 cast(ZoneNumber)1
 to!ZoneNumber(1)
=20 Use `to` except where you can gaurantee that the input value maps=20 to a valid enum member, because `cast` does not check your work: =20 writeln(cast(ZoneNumber)17); // breaks the type system writeln(to!ZoneNumber(17)); // throws a ConvException: Value=20 (17) does not match any member value of enum 'ZoneNumber' =20 So, `cast` is faster, but unsafe. `to` is slower, but protects=20 the enum's invariant.
Thanks. It seems to! is de rigueur in this case. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D 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
Apr 21 2020
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Apr 21 2020