UDAs on enum members

apz28 via Digitalmars-d digitalmars-d at puremagic.com
Thu Aug 31 20:54:40 PDT 2017


On Wednesday, 13 July 2016 at 11:57:21 UTC, Tomer Filiba wrote:
> It would be really nice if I could put UDAs on enum members as 
> well, e.g.,
>
> enum MyEnum {
>     @("SOM") SomeMember,
>     @("ANO") AnotherMemberWithAVeryLongName,
> }
>
> I can think of many reasons why that would be desired, but the 
> concrete one is the following: I have an interchangeable data 
> format, and my enum might gain members over time. I don't care 
> about the value of the member, so I don't want to number them 
> myself, but I can't control where users would choose to place 
> the new member, so they might cause renumbering of existing 
> members, breaking the interchangeable format.
>
> So what I wanted is to assign each member a "short stable name" 
> that would be used to serialize the value (using my own 
> dump/load functions)... But I had to resort to a long, ugly 
> switch statement, mapping members to their names and vice 
> versa. The dumping function uses final-switch, so you won't 
> forget to update it, but the loading one can't (it takes a 
> string) so it would be easy to people to forget.
>
> Given that UDAs can be used practically everywhere (including 
> struct/union members), is there an objection to make them legal 
> on enum members as well?
>
> And while we're on the subject, why can't enums have methods? 
> At the risk of sounding as if I like Java (I don't :) ), it's a 
> really nice language feature. Back to our example:
>
> enum MyEnum {
>     @("SOM") SomeMember,
>     @("ANO") AnotherMemberWithAVeryLongName;
>
>     string dump() {
>         ...  // `this` is a value, not a ref here
>     }
>     static MyEnum load(string name) {
>         ...
>     }
> }
>
> Basically just allow a semicolon at the end of the members, 
> after which methods could appear. Adding members or whatever 
> else Java has is an overkill -- just use a struct for that. But 
> instead of lots of dumpMyEnum(MyEnum e)/loadMyEnum(string s) 
> pairs, you could write myMember.dump()/MyEnum.load(s)
>
>
> -tomer

Here how I implement it

struct EnumArray(E, V)
{
nothrow @safe:

public:
     struct Entry
     {
         E e;
         V v;
     }

private:
     enum isEntryType(T) = is(Entry == T);
     enum size = EnumMembers!E.length;
     V[size] _values;

public:
     this(T...)(T aValues)
     if (allSatisfy!(isEntryType, T))
     {
         foreach (ref Entry i; aValues)
             _values[i.e] = i.v;
     }

     V opIndex(E aEnum) const
     {
         return _values[aEnum];
     }

     V opIndexAssign(V aValue, E aEnum)
     {
         return _values[aEnum] = aValue;
     }

     V opDispatch(string aEnumName)() const
     {
         import std.conv : to;

         enum e = aEnumName.to!E;
         return _values[e];
     }

     version (none)
     V opDispatch(string aEnumName)(V aValue)
     {
         import std.conv : to;

         enum e = aEnumName.to!E;
         return _values[e] = aValue;
     }

     E getEnum(V aValue, E aDefault = E.min)
     {
         foreach (i; EnumMembers!E)
         {
             if (_values[i] == aValue)
                 return i;
         }

         return aDefault;
     }

@property:
     size_t length() const
     {
         return size;
     }
}

unittest // EnumArray
{
     enum EnumTest
     {
         one,
         two,
         max
     }

     alias EnumTestInt = EnumArray!(EnumTest, int);

     EnumTestInt testInt = EnumTestInt(
         EnumTestInt.Entry(EnumTest.one, 1),
         EnumTestInt.Entry(EnumTest.two, 2),
         EnumTestInt.Entry(EnumTest.max, int.max)
     );

     assert(testInt.one == 1);
     assert(testInt.two == 2);
     assert(testInt.max == int.max);

     assert(testInt[EnumTest.one] == 1);
     assert(testInt[EnumTest.two] == 2);
     assert(testInt[EnumTest.max] == int.max);

     assert(testInt.getEnum(1) == EnumTest.one);
     assert(testInt.getEnum(2) == EnumTest.two);
     assert(testInt.getEnum(int.max) == EnumTest.max);
     assert(testInt.getEnum(3) == EnumTest.one); // Unknown -> 
return default min


     alias EnumTestString = EnumArray!(EnumTest, string);

     EnumTestString testString = EnumTestString(
         EnumTestString.Entry(EnumTest.one, "1"),
         EnumTestString.Entry(EnumTest.two, "2"),
         EnumTestString.Entry(EnumTest.max, "int.max")
     );

     assert(testString[EnumTest.one] == "1");
     assert(testString[EnumTest.two] == "2");
     assert(testString[EnumTest.max] == "int.max");

     assert(testString.getEnum("1") == EnumTest.one);
     assert(testString.getEnum("2") == EnumTest.two);
     assert(testString.getEnum("int.max") == EnumTest.max);
     assert(testString.getEnum("3") == EnumTest.one); // Unknown 
-> return default min
}

Pham



More information about the Digitalmars-d mailing list