www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Comparison of Enumerations with base type of String?

reply Michael Reiland <Michael.Reiland gmail.com> writes:
Hey guys,

I was running through a tutorial and I noticed that enums can 
have a base type of string.  Which is interesting, but I'm 
wondering about comparisons.

I'm guessing the comparison boils down to a pointer comparison, 
but I thought I'd confirm.
Aug 26 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, August 27, 2017 01:43:14 Michael Reiland via Digitalmars-d-learn 
wrote:
 Hey guys,

 I was running through a tutorial and I noticed that enums can
 have a base type of string.  Which is interesting, but I'm
 wondering about comparisons.

 I'm guessing the comparison boils down to a pointer comparison,
 but I thought I'd confirm.
No, it'll do the normal string comparison, just like enums that are ints do a normal integer comparison. The fact the strings are enums doesn't really change much except for stuff that's specifically doing something differently for enums. It just means that instead of having separate constants which are strings, you have them grouped together, and final switch statements will require that the cases cover all of them. Some stuff like std.conv.to!string or writeln will end up using the names of the enums rather than their values like they do with other enums even though they're strings, but in general, having enum S : string { foo = "yes", bar = "no", } isn't all that different from just doing enum foo = "yes"; enum bar = "no"; except that declaring the enum S essentially puts them in a namespace called S. It does create a new type S, which affects some stuff (e.g. S is not a range, whereas string is), so operating an an S is not completely identical to operating on a string, but S doesn't magically end up implementing operations differently than occurs with string. It's just that some of the operations on S are more restricted than a naked string, and since they implicitly convert to string rather than being a string, some functions that looks at the type will operate on an S differently than they would for a naked string. - Jonathan M Davis
Aug 26 2017
parent reply Michael Reiland <Michael.Reiland gmail.com> writes:
You can randomly assign a string to an enum?  Or am I 
misunderstanding that last bit of code?

Also it sounds to me like string enums are going to be slower 
performance wise than integer enums.
Aug 26 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, August 27, 2017 03:47:58 Michael Reiland via Digitalmars-d-learn 
wrote:
 You can randomly assign a string to an enum?  Or am I
 misunderstanding that last bit of code?
No, you can't directly assign a string to an enum of type string. That's part of why they don't pass isInputRange. This code fails to compile enum S : string { a = "foo", b = "bar" } void main() { S s = "baz"; } though you can force it with a cast, e.g. S s = cast(S)"baz"; Unfortunately however, enums in D really aren't very strongly typed. So, while direct assignment will fail, stuff like S s = S.a; s ~= "blah"; will compile. And enums of base type int have the same problem - e.g. E e = E.a | E.b; In both cases, you trivially end up with a value that is not valid for that enum type and will likely cause problems with final switch (which will result in an Error being thrown when not compiling with -release but will do who-knows-what when -release is used, because there is no check with -release). The result is that a variable of an enum type does not entirely act like the base type even when you're not considering stuff like using is expressions or std.traits to test something based on its type. It's implicitly convertible to its base type but is not its base type. However, a bunch of operations that IMHO really should not be legal on an enum are (e.g. appending), and some which could be legal but should result in the base type rather than the enum type (e.g concatenation or |ing) result in the enum type, making it trivial to create enum variables with values that aren't in the enum if you're not careful. Discussions have been had on this in the past, and I'm firmly of the belief that the current design is a mistake in this regard, but some (Andrei included) have argued that it's perfectly legitimate to have an enum variable which is not one of the listed enum values with the idea that you're defining a general type that has a few known constants but the constants aren't all of the values. I think this violates the idea of what an enum is (I think that an enum should always define all valid values for that enum and no other values should be legal), and it definitely doesn't play nicely with final switch, but for the moment, we're stuck.
 Also it sounds to me like string enums are going to be slower
 performance wise than integer enums.
I expect so. How efficient an enum is depends entirely on what its base type is and what you're doing with it. However, if all you're really looking for is to have a string representation of the name of an enum, then you can have the base type be int, and to!string and writeln will give you the string representation of the name of the enum without the enum needing to be a string. Whether it makes sense to use string as the base type of an enum depends entirely on what you're trying to do with the enum, just like whether you use a struct as the base type of an enum makes sense depends on what you're doing. Sometimes, it can be very useful, but that doesn't mean that it's always the best solution. - Jonathan M Davis
Aug 26 2017
parent reply Michael Reiland <Michael.Reiland gmail.com> writes:
whoa, you can use a struct as a basetype for an enum?  I'm 
guessing it allows you to associate more information with the 
enum without using lookup tables and the like?  And equality is 
probably just a memberwise comparison of the struct itself?

That seems interesting like an interesting idea, although I'm not 
sure if I'd ever use it.

I'm mostly just trying to get a feel for how things work 
underneath so I have a general understanding of the performance 
implications of things (in a very general sense).

Thanks for the indepth responses, I've learned a few things from 
them.
Aug 27 2017
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, August 27, 2017 22:01:52 Michael Reiland via Digitalmars-d-learn 
wrote:
 whoa, you can use a struct as a basetype for an enum?  I'm
 guessing it allows you to associate more information with the
 enum without using lookup tables and the like?  And equality is
 probably just a memberwise comparison of the struct itself?

 That seems interesting like an interesting idea, although I'm not
 sure if I'd ever use it.
Yeah, you can use pretty much any type as a base type for an enum - though once you're dealing with heap-allocated objects, it gets a bit entertaining, just like it does with enums that are manifest constants. e.g. enum a = [1, 2, 3]; will allocate that array every time that you use the enum. I don't think that mutable or const classes will work, but you can also use immutable classes, which should not result in an allocation every time (since they're immutable, they can be put in the read-only segment by the compiler like with string literals), but using structs almost certainly makes more sense if you really want a user-defined type. Basically, any time you have a group of constants that you want to treat as a group of constants, an enum can make sense, even if those constants are structs. Java implemented enums as classes, so this enables similar capabilities to what you could do with enums in Java. I can't remember if I've ever used a enum with a base type of struct though. Usually, I either need ints or strings. I've definitely used manifest constants that are structs, but actual enums that are structs are not something that I'v frequently found useful, much as I'm glad that it's possible. Ultimately, enums really aren't all that different from anything else with the same base type except that you can't take their address, they're basically copy-pasted wherever they're used (though it's the value that's copy-pasted, not the expression used to create the value - so you wouldn't end up with something like a struct constructor being called), some operations aren't legal on them which are legal on the base type (mostly assignment from non-enums), and anything which explicitly checks that they're an enum may treat them differently. But overall, you'll mostly get out of an enum what you'd expect from the base type. The only caveat that I can think of at the moment with regards to performance is that dynamic arrays (other than strings) result in an allocation every time they're used, which can be surprising if you're not aware of it, and that applies to manifest constants as well as proper enums. Really, manifest constants and proper enums are pretty much the same thing except that proper enums declare an actual type with a known set of values, whereas a manifest constant retains its original type. The performance implications should be the same though. And the fact that they're so similar is part of why the enum keyword was reused, even though that annoys some folks, since a manifest constant isn't really an enum. - Jonathan M Davis
Aug 27 2017