www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Casting from an enum type to another enum type

reply "Roland Hadinger" <rolandh.dlangforum maildrop.cc> writes:
Hi!

What is the straightest way to safely cast from one enum type A 
to another enum type B, when B, in terms of values as well as 
identifiers, is a strict subset of A?

Ideally, that should be as simple as "to!B(a)". So far I've tried 
a couple of things, and one way I found was to first cast A to 
int, then cast that to B, but that is not really straightforward.

Example:

import std.stdio;
import std.conv;

enum Color { r, o, y, g, b, i, v }

enum StyleColor : int { o = Color.o, b = Color.b }

void main()
{
     auto c = Color.g;

     // Problem: the result is not guaranteed to be a StyleColor.
     // Does not throw, needs extra checks.
     auto d1 = cast(StyleColor) c;

     // Typesafe, but is not exactly straightforward.
     auto d2 = to!StyleColor(cast(int) c);

     // Error: template std.conv.toImpl cannot deduce
     // function from argument types !(StyleColor)(Color)
     //
     // Changing "enum StyleColor : Color" to "enum StyleColor : 
int"
     // in the definition above does not help either.
     auto d3 = to!StyleColor(c);
}
Jun 24 2015
parent reply "Meta" <jared771 gmail.com> writes:
On Wednesday, 24 June 2015 at 15:29:03 UTC, Roland Hadinger wrote:
 Hi!

 What is the straightest way to safely cast from one enum type A 
 to another enum type B, when B, in terms of values as well as 
 identifiers, is a strict subset of A?

 Ideally, that should be as simple as "to!B(a)". So far I've 
 tried a couple of things, and one way I found was to first cast 
 A to int, then cast that to B, but that is not really 
 straightforward.

 Example:

 import std.stdio;
 import std.conv;

 enum Color { r, o, y, g, b, i, v }

 enum StyleColor : int { o = Color.o, b = Color.b }

 void main()
 {
     auto c = Color.g;

     // Problem: the result is not guaranteed to be a StyleColor.
     // Does not throw, needs extra checks.
     auto d1 = cast(StyleColor) c;

     // Typesafe, but is not exactly straightforward.
     auto d2 = to!StyleColor(cast(int) c);

     // Error: template std.conv.toImpl cannot deduce
     // function from argument types !(StyleColor)(Color)
     //
     // Changing "enum StyleColor : Color" to "enum StyleColor : 
 int"
     // in the definition above does not help either.
     auto d3 = to!StyleColor(c);
 }
std.conv.to really should be able to do this, but I guess not many people have needed to do this. You can write an "extension" to `to` which does it for you: import std.traits; auto to(To, From)(From f) if (is(From == enum) && is(To == enum) && is(OriginalType!From : OriginalType!To)) { return cast(To)f; } enum Color { r, o, y, g, b, i, v } enum StyleColor : int { o = Color.o, b = Color.b } void main() { auto c = Color.g; auto s = c.to!StyleColor; }
Jun 24 2015
next sibling parent "Meta" <jared771 gmail.com> writes:
Note that this is a very simple example. You need to check in the 
function that a valid StyleColor will actually be produced. 
Otherwise, it'll happily produce a StyleColor that's invalid.
Jun 24 2015
prev sibling parent "Roland Hadinger" <rolandh.dlangforum maildrop.cc> writes:
On Wednesday, 24 June 2015 at 18:16:42 UTC, Meta wrote:
 std.conv.to really should be able to do this, but I guess not 
 many people have needed to do this. You can write an 
 "extension" to `to` which does it for you:

 import std.traits;

 auto to(To, From)(From f)
 if (is(From == enum) && is(To == enum) && is(OriginalType!From 
 : OriginalType!To))
 {
     return cast(To)f;
 }
Thanks, that works! I'll add this to my project's 'helpers' module, with the "return" line replaced by: foreach (m; EnumMembers!To) { if (m == f) return m; } throw new ConvException("Value not in enum");
Jun 24 2015