reflective enums

Kevin Bealer kevinbealer at gmail.com
Fri Feb 16 09:28:54 PST 2007


== Quote from renoX (renosky at free.fr)'s article
> Kevin Bealer Wrote:
...
> - why do Enum start the values at 1 instead of 0?
>
> IMHO it should follow the way 'enum' works otherwise developers may be confused.

You're right -- this should match the default enum{} behavior.  I actually did it
this way as a kind of shorthand for myself.  When I write an enum {} I usually add
a starting value with a name like "e_None" or something.  You could think of it as
comparable to the floating point NaN.  If I don't set an enumerated type, I want
it to have an out-of-bound value.

> - why the name getString instead of the usual toString name?

That could be changed too; I think of toString() as an object method, and
when adding a static method, I figured I should use a different name to
avoid conflicting with the method.  I didn't really check whether there
is a real conflict on this.

There is kind of a strangeness with the way I defined this in that you
creating instances of the Enum!(...) struct is not useful.  The type is
only really interesting for its static properties.

> - I'm not sure if it's useful to 'optimise' compilation time, but there were a
few call to SplitChomp that can be replaced by SplitFirst.
> - toFullString which returns "<enum name>(<enum value>)" can be useful too
maybe, I wanted to do a concatenation reusing toString to avoid duplicating the
code, but I didn't manage to, so I added a parameter instead.
> The code below has the modifications.
> I wonder if the reflective enums could be integrated into a library, I think
that it would be useful.
> The only downside of this version is that the developer must use int instead of
a separate type.. This is probably fixable.

These modifications make sense.  I'm thinking I can write a much smaller and more
straightforward
version with 1.006.

> renoX
> import std.stdio;
> import std.metastrings;
> template Find(char[] A, char[] B) {
>     static if (A.length < B.length)
> 	{
>         const int Find = -1;
>     } else static if (A[0..B.length] == B) {
>         const int Find = 0;
>     } else static if (-1 == Find!(A[1..$], B)) {
>         const int Find = -1;
>     } else {
>         const int Find = 1 + Find!(A[1..$], B);
>     }
> }
> template SplitFirst(char[] A, char[] B) {
>     const int Location = Find!(A, B);
>     static if (Location == -1) {
>         const char[] First = A;
>         const char[] Rest = "";
>     } else {
>         const char[] First = A[0..Location];
>         const char[] Rest = A[Location+B.length..$];
>     }
> }
> template ChompSpaces(char[] A) {
>     static if (A.length) {
>         static if (A[0] == ' ') {
>             alias ChompSpaces!(A[1..$]) ChompSpaces;
>         } else static if (A[$-1] == ' ') {
>             alias ChompSpaces!(A[0..$-1]) ChompSpaces;
>         } else {
>             alias A ChompSpaces;
>         }
>     } else {
>         const char[] ChompSpaces = "";
>     }
> }
> template SplitChomp(char[] A, char[] B) {
>     alias ChompSpaces!(SplitFirst!(A, B).First) First;
>     alias ChompSpaces!(SplitFirst!(A, B).Rest) Rest;
> }
> template EnumName(char[] A) {
>     alias SplitChomp!(SplitFirst!(A, ",").First, "=").First EnumName;
> }
> template EnumAssign(char[] A) {
>     alias SplitChomp!(SplitFirst!(A, ",").First, "=").Rest EnumAssign;
> }
> template EnumValue(char[] A, int i) {
>     static if (EnumAssign!(A) == "") {
>         const int EnumValue = i;
>     } else {
>         const int EnumValue =
>             mixin(ParseInteger!(EnumAssign!(A)).value);
>     }
> }
> template BuildOneEnum(char[] A, int i) {
>     const char[] BuildOneEnum =
>         "const int "~EnumName!(A)~" = "~
>         ToString!(EnumValue!(A, i))~";\n";
> }
> template BuildEnums(char[] A, int i) {
>     static if (SplitChomp!(A, ",").Rest.length == 0) {
>         const char[] BuildEnums = BuildOneEnum!(A, EnumValue!(A, i));
>     } else {
>         const char[] BuildEnums =
>             BuildOneEnum!(SplitChomp!(A, ",").First, EnumValue!(A, i)) ~
>             BuildEnums!(SplitChomp!(A, ",").Rest, EnumValue!(A, i)+1);
>     }
> }
> template BuildOneCase(char[] A, int i, bool full) {
> 	static if (!full) {
> 	    const char[] BuildOneCase =
>     	    "case "~ToString!(EnumValue!(A, i))~
>         	": return \""~EnumName!(A)~"\";\n";
> 	} else {
> 	    const char[] BuildOneCase =
>     	    "case "~ToString!(EnumValue!(A, i))~
>         	": return \""~EnumName!(A)~"("~ToString!(EnumValue!(A, i))~")\";\n";
> 	}
> }
> template BuildEnumCases(char[] A, int i, bool full) {
>     static if (SplitChomp!(A, ",").Rest.length == 0) {
>         const char[] BuildEnumCases =
>             BuildOneCase!(A, EnumValue!(A, i), full);
>     } else {
>         const char[] BuildEnumCases =
>             BuildOneCase!(SplitChomp!(A, ",").First,
>                           EnumValue!(A, i), full) ~
>             BuildEnumCases!(SplitChomp!(A, ",").Rest,
>                             EnumValue!(A, i)+1,full);
>     }
> }
> template BuildEnumSwitch(char[] A, int i, bool full) {
>     const char[] BuildEnumSwitch =
>         "switch(x) {"~
>         BuildEnumCases!(A, i, full) ~
>         "default: "~
>         "    throw new Exception(\"enumeration out of range\");"
>         "}";
> }
> template BuildOneApply(char[] A, int i) {
>     const char[] BuildOneApply =
>         "i="~ToString!(EnumValue!(A, i))~";"~
>         " rv=dg(i); if (rv) return rv;";
> }
> template BuildEnumApply(char[] A, int i) {
>     static if (SplitChomp!(A, ",").Rest.length == 0) {
>         const char[] BuildEnumApply =
>             BuildOneApply!(A, EnumValue!(A, i));
>     } else {
>         const char[] BuildEnumApply =
>             BuildOneApply!(SplitChomp!(A, ",").First,
>                            EnumValue!(A, i))~
>             BuildEnumApply!(SplitChomp!(A, ",").Rest,
>                             EnumValue!(A, i)+1);
>     }
> }
> struct Enum(char[] A)
> {
>     mixin(BuildEnums!(A, 0));
>     static char[] toString(int x)
>     {
>         mixin(BuildEnumSwitch!(A, 0, false));
>     }
>     static char[] toFullString(int x)
>     {
>         mixin(BuildEnumSwitch!(A, 0, true));
>     }
>     int opApply(int delegate(inout int) dg)
>     {
>         int i, rv;
>         mixin(BuildEnumApply!(A, 0));
>         return 0;
>     }
> }
> int main(char[][] args)
> {
>     alias Enum!("start, middle, end=10, ps, pps,") PState;
>     int s = PState.start;
> 	int m = PState.middle;
>     writefln("s is %s, with name %s %s\n", s, PState.toString(s),
PState.toFullString(s));
>     writefln("m is %s, with name %s %s\n", m, PState.toString(m),
PState.toFullString(m));
>     PState P;
>     foreach(v; P) {
>         writefln("Enum %s has name=%s and %s", v, PState.toString(v),
PState.toFullString(v));
>     }
>     return 0;
> }




More information about the Digitalmars-d mailing list