Subtyping of an enum

Alex AJ at gmail.com
Mon Apr 15 21:27:06 UTC 2019


On Monday, 15 April 2019 at 20:36:09 UTC, Anton Fediushin wrote:
> On Monday, 15 April 2019 at 14:20:57 UTC, Alex wrote:
>> On Monday, 15 April 2019 at 08:39:24 UTC, Anton Fediushin 
>> wrote:
>>> Hello! I am currently trying to add a custom `toString` 
>>> method to an enum so that:
>>> 1. Enum members would still have numeric values and can be 
>>> easily compared (things like `enum a { foo = "FOO", bar = 
>>> "BAR”}` won't do, I want `a.foo < a.bar)`)
>>> 2. More custom methods can be implemented in the future
>>>
>>> Obvious solution is to wrap an enum in a structure and 
>>> utilize 'alias this' for subtyping like this:
>>> ```
>>> struct Enum {
>>>   private {
>>>     enum internal {
>>>       foo,
>>>       bar
>>>     }
>>>     internal m_enum;
>>>   }
>>>   this(internal i) { m_enum = i; }
>>>   alias m_enum this;
>>>   string toString() {
>>>     // custom implementation of toString
>>>   }
>>> }
>>> ```
>>>
>>> This seems to work just fine for assigning and comparisons 
>>> but passing Enum as a function argument does not work:
>>> ```
>>> void fun(Enum e) {}
>>>
>>> fun(Enum.foo);
>>> ---
>>> Error: function fun(Enum e) is not callable using argument 
>>> types (internal)
>>> Cannot pass argument foo of type internal to parameter Enum e.
>>> ```
>>>
>>> Of course, I could just define a bunch of functions that 
>>> accept my enum as the first argument and call them using UFCS 
>>> but it'd require to explicitly specify functions instead of D 
>>> taking care of that (toString() for structures is called 
>>> automagically by functions like writeln) and those functions 
>>> would hang around here and there totally unorganized. I 
>>> prefer to keep functions inside of structures and classes.
>>>
>>> If there are other ways of achieving the same *and* keeping 
>>> code clean and organized, please share.
>>>
>>> Thank you in advance,
>>> Anton.
>>
>> yes,
>>
>> import std.stdio, std.meta, std.traits, std.conv;
>>
>> enum _MyEnum : int { a,b,c}
>>
>> struct _Enum(T)
>> {
>>    T value;
>>    alias value this;
>>    // generate static field members
>>    static foreach(e, v; EnumMembers!T)
>>    {
>>      pragma(msg, "static MyEnum "~to!string(v)~" = 
>> MyEnum(T."~to!string(v)~");");
>>      mixin("static MyEnum "~to!string(v)~" = 
>> cast(MyEnum)(T."~to!string(v)~");");
>>    }
>> }
>>
>> alias _Enum!_MyEnum MyEnum;
>>
>> void foo(MyEnum e)
>> {
>>    writeln(to!int(e));
>> }
>> void main()
>> {
>>     foo(MyEnum.a);
>>     foo(MyEnum.b);
>>     foo(MyEnum.c);
>> }
>>
>> https://run.dlang.io/is/WOcLrZ
>>
>> Note that value is never used, it just makes the cast work and 
>> treats the struct as an enum. Not sure if there is a way 
>> around that.
>
> Thank you, this is the solution I have been looking for!

Yes, it is quite nice. D should simply allow enums to act as 
structs, they are effectively the same.[meaning they should 
should allow methods having methods]

You might want to add a little more functionality. I screwed up 
the code when generalizing it, here is a bit better version:

https://run.dlang.io/is/yyuy77

Using the struct you can leverage all the power of op's such as 
dispatching, assignment, etc.

Essentially one is just using a standard struct but using enum's 
to define the names and values(since they are auto filled).

D should just do away with enums and allow one to create a enum 
struct, e.g.,

struct S
{
     enum : int
     {
        a,b,c
     }
}

Of which enum S : int { a,b,c } is short hand when one does not 
want to add new functionality or D could just treat enum like a 
struct by allowing methods inside the enum.

The code I created essentially emulates this, one can make the 
enum internal and private to hide it.


More information about the Digitalmars-d-learn mailing list