www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Getting enum from value

reply Matthew Remmel <matt.remmel yahoo.com> writes:
I feel like I'm missing something, but there has to be an easier 
way to convert a value into an enum than switching over every 
possible value: i.e

enum Capitals {
     Indiana = "Indianapolis",
     Illinois = "Chicago",
     Ohio = "Columbus"
}

Capitals enumFromValue(string s) {
     switch (s) {
         case Capitals.Indiana:
             return Capitals.Indiana;
          case Capitals.Illinois:
             return Capitals.Illinois;
          case Capitals.Ohio:
             return Capitals.Ohio;
          default:
              throw new Exception(format("No Capitals enum member 
with value %s", s));
     }
}

int main() {
     Capitals c = enumFromValue("Chicago"); // works

     // I tried using std.conv, but it matches on the enum member 
name
     c = to!Capitals("Chicago") // fails, no member named Chicago
}

With how redundant the enumFromValue(string) implementation is, I 
would think there would be an easier way to do it. I'm sure you 
could use a mixin, a template, or std.traits. I was hoping there 
was a more 'builtin' way to do it though. Something along the 
simplicity of:

int main() {
     Capitals c = Capitals("Chicago");
}

Any ideas?
Aug 05
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel wrote:
 I feel like I'm missing something, but there has to be an 
 easier way to convert a value into an enum than switching over 
 every possible value: i.e

 [...]
What you want is already in the standard library. std.conv.to can convert strings to enums and back.
Aug 05
prev sibling next sibling parent reply Rene Zwanenburg <renezwanenburg gmail.com> writes:
On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel wrote:
 Any ideas?
You can use to! in std.conv: import std.stdio; import std.conv; enum Foo { A = "A", B = "B" } void main() { writeln("A".to!Foo); }
Aug 05
next sibling parent Temtaime <temtaime gmail.com> writes:
On Saturday, 5 August 2017 at 15:42:53 UTC, Rene Zwanenburg wrote:
 On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel 
 wrote:
 Any ideas?
You can use to! in std.conv: import std.stdio; import std.conv; enum Foo { A = "A", B = "B" } void main() { writeln("A".to!Foo); }
Are you fools ? Did you ever read the post ? I think this is a minimal solution: enum Foo { A = "AV", B = "BV", C = "CV", } Foo K = [ EnumMembers!Foo ].find!(a => a == `BV`)[0];
Aug 05
prev sibling parent Matthew Remmel <matt.remmel yahoo.com> writes:
On Saturday, 5 August 2017 at 15:42:53 UTC, Rene Zwanenburg wrote:
 On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel 
 wrote:
 Any ideas?
You can use to! in std.conv: import std.stdio; import std.conv; enum Foo { A = "A", B = "B" } void main() { writeln("A".to!Foo); }
This only works because the enum name and the value are the same. Its actually converting on the enum name, which happens to be the same as the value. This doesn't work if the values is different: enum Foo { A = "AV", B = "BV" } int main() { writeln("AV".to!Foo); // Throws exceptions return 0; } It looks like Temtaime's solution works:
enum Foo
{
	A = "AV",
	B = "BV",
	C = "CV",
}
Foo K = [ EnumMembers!Foo ].find!(a => a == `BV`)[0];
I can probably make a template or something out of this to make the syntax simpler.
Aug 05
prev sibling next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 08/05/2017 05:33 PM, Matthew Remmel wrote:
 I feel like I'm missing something, but there has to be an easier way to 
 convert a value into an enum than switching over every possible value: i.e
 
 enum Capitals {
      Indiana = "Indianapolis",
      Illinois = "Chicago",
      Ohio = "Columbus"
 }
 
 Capitals enumFromValue(string s) {
      switch (s) {
          case Capitals.Indiana:
              return Capitals.Indiana;
           case Capitals.Illinois:
              return Capitals.Illinois;
           case Capitals.Ohio:
              return Capitals.Ohio;
           default:
               throw new Exception(format("No Capitals enum member with 
 value %s", s));
      }
 }
 
 int main() {
      Capitals c = enumFromValue("Chicago"); // works
 
      // I tried using std.conv, but it matches on the enum member name
      c = to!Capitals("Chicago") // fails, no member named Chicago
 }
 
 With how redundant the enumFromValue(string) implementation is, I would 
 think there would be an easier way to do it. I'm sure you could use a 
 mixin, a template, or std.traits. I was hoping there was a more 
 'builtin' way to do it though. Something along the simplicity of:
 
 int main() {
      Capitals c = Capitals("Chicago");
 }
 
 Any ideas?
As far as I know, there's no built-in way to do this. But you can simplify and generalize your `enumFromValue`: ---- enum Capitals { Indiana = "Indianapolis", Illinois = "Chicago", Ohio = "Columbus" } E enumFromValue(E)(string s) { import std.format: format; import std.traits: EnumMembers; switch (s) { foreach (c; EnumMembers!E) { case c: return c; } default: immutable string msgfmt = "enum %s has no member with value %s"; throw new Exception(format(msgfmt, E.stringof, s)); } } void main() { auto c = enumFromValue!Capitals("Chicago"); assert(c == Capitals.Illinois); } ----
Aug 05
parent ag0aep6g <anonymous example.com> writes:
On 08/05/2017 07:05 PM, ag0aep6g wrote:
 E enumFromValue(E)(string s)
The type of `s` should probably be a template parameter as well.
Aug 05
prev sibling parent reply Kreikey <rick.kreikebaum gmail.com> writes:
On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel wrote:
 I feel like I'm missing something, but there has to be an 
 easier way to convert a value into an enum than switching over 
 every possible value: i.e

 [...]
Capitals c = cast(Capitals)"Chicago"; writeln(c); // Illinois
Aug 05
parent reply Matthew Remmel <matt.remmel yahoo.com> writes:
On Saturday, 5 August 2017 at 18:26:10 UTC, Kreikey wrote:
 On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel 
 wrote:
 I feel like I'm missing something, but there has to be an 
 easier way to convert a value into an enum than switching over 
 every possible value: i.e

 [...]
Capitals c = cast(Capitals)"Chicago"; writeln(c); // Illinois
I'm annoyed that I didn't think of trying to cast it. That works great if the value exists in the enum. It does something weird if the value doesn't though. This is my test.d file: import std.stdio; enum Foo { A = "AV", B = "BV" } void main() { Foo k = cast(Foo)"BV"; // Works and prints correctly k = cast(Foo)"CV"; writeln("Type: ", typeid(k)); // Type: test.Foo writeln("Value: ", k); // Value: cast(Foo)CV } -------- The output shows the type being the Foo enum but the value is 'cast(Foo)CV'. I would of expected an error or exception to be thrown if it wasn't able to cast into an actual enum member. Is this something with how the enums are implemented under the hood?
Aug 05
next sibling parent Kreikey <rick.kreikebaum gmail.com> writes:
On Saturday, 5 August 2017 at 20:11:27 UTC, Matthew Remmel wrote:
 On Saturday, 5 August 2017 at 18:26:10 UTC, Kreikey wrote:
 On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel
I'm annoyed that I didn't think of trying to cast it. That works great if the value exists in the enum. It does something weird if the value doesn't though. This is my test.d file: import std.stdio; enum Foo { A = "AV", B = "BV" } void main() { Foo k = cast(Foo)"BV"; // Works and prints correctly k = cast(Foo)"CV"; writeln("Type: ", typeid(k)); // Type: test.Foo writeln("Value: ", k); // Value: cast(Foo)CV } -------- The output shows the type being the Foo enum but the value is 'cast(Foo)CV'. I would of expected an error or exception to be thrown if it wasn't able to cast into an actual enum member. Is this something with how the enums are implemented under the hood?
That was my first post on this forum, so I'm glad it was at least a little bit useful :-D I think the reasoning for no error on bad casts is that casting is a blunt instrument that assumes the programmer knows what he's doing, and it breaks the type system. So you'd want to use one of the aforementioned solutions if you're set on using enums in this way. You might also consider using associative arrays, but it's also a bit cumbersome. There's no way to get around searching: capitals = [ "Indiana" : "Indianapolis", "Illinois" : "Chicago", "Ohio" : "Columbus" ]; auto r = capitals.byKeyValue.find!((a, b) => a.value == b)("Chicago"); if (!r.empty) { writeln(capitals[r.front.key]); } else { writeln("not found"); } You could also define another associative array statesByCapital with the key : value orders reversed, and then you could also do statesByCapitol["Chicago"]. Of course then you'd have to keep things in sync if things change. But I discovered a neat trick you could use to generate such a two way mapping. You could define one array string[] capitals, and another array string[] states. Then you could do: auto capitalsByState = assocArray(zip(states, capitals)); auto statesByCapital = assocArray(zip(capitals, states)); If your data doesn't change for the lifetime of the program, that looks like a nice way to do it.
Aug 05
prev sibling parent Kreikey <rick.kreikebaum gmail.com> writes:
On Saturday, 5 August 2017 at 20:11:27 UTC, Matthew Remmel wrote:
 On Saturday, 5 August 2017 at 18:26:10 UTC, Kreikey wrote:
 On Saturday, 5 August 2017 at 15:33:57 UTC, Matthew Remmel 
 wrote:
 I feel like I'm missing something, but there has to be an 
 easier way to convert a value into an enum than switching 
 over every possible value: i.e

 [...]
Capitals c = cast(Capitals)"Chicago"; writeln(c); // Illinois
I'm annoyed that I didn't think of trying to cast it. That works great if the value exists in the enum. It does something weird if the value doesn't though. This is my test.d file: import std.stdio; enum Foo { A = "AV", B = "BV" } void main() { Foo k = cast(Foo)"BV"; // Works and prints correctly k = cast(Foo)"CV"; writeln("Type: ", typeid(k)); // Type: test.Foo writeln("Value: ", k); // Value: cast(Foo)CV } -------- The output shows the type being the Foo enum but the value is 'cast(Foo)CV'. I would of expected an error or exception to be thrown if it wasn't able to cast into an actual enum member. Is this something with how the enums are implemented under the hood?
So I've come up with a concise way to do this. Given: Capitals strToEnum(string myString) { Capitals instance = cast(Capitals)myString; if (![EnumMembers!Capitals].canFind(instance)) throw new Exception("can't convert that string to that enum"); return instance; } do: Capitals c = strToEnum("Chicagoo"); Not quite a one-liner, but pretty close. Turning it into a template is left to the user as an exercise ;)
Aug 06