reflective enums

renoX renosky at free.fr
Fri Feb 16 08:14:21 PST 2007


Kevin Bealer Wrote:
> People have occasionally asked here about being able to do things like 
> foreach() over the value of the enums.  The following code is probably 
> obsolete now that 1.006 is out, but I'm posting it anyway.

Very nice, thanks!
I was trying to do the same thing (without much success, I'm not used to functional template programming) , so you saved me quite some time..

I have a few remarks:
- why do Enum start the values at 1 instead of 0?
IMHO it should follow the way 'enum' works otherwise developers may be confused.

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

- 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.

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