Design of reflective enums

renoX renosky at free.fr
Mon Feb 19 09:02:30 PST 2007


Michiel Wrote:

> renoX wrote:
> 
> > Hello,
> > Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible.
> > Reflective enums have two useful properties that don't have normal enums:
> > - their label is printable
> > - it's possible to do a foreach on all the possible values.
> 
> By the way, should this not be in the core language?

Well I think that Ada does it in the core language (but I think that they are not printable), it is able to define enum as an index array as you suggest in your second email.
As the more I look at D, the more I find it similar to Ada, it is perhaps a good idea to make them those enhanced enum in the core language, and as an improvement to Ada those enum should be printable (maybe as an option to save memory) .

In the meantime, my trial is below, but it doesn't work when there are two enums: the two toString conflicts, even though they should not because their parameter is a different enum type.
Should I report this as a bug?

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 EnumListName(char[] A) {
    alias SplitChomp!(A, "{").First EnumListName;
}

template EnumBody(char[] A) {
    alias SplitChomp!(SplitFirst!(A, "}").First, "{").Rest EnumBody;
}


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 BuildEnumElem(char[] A, char[]enumListName, int i, int ekv) {
	static if (ekv == 0) {
		const char[] BuildEnumElem =
			enumListName~"."~EnumName!(A)~", ";
	} ;
	static if (ekv == 1) {
			const char[] BuildEnumElem =
			"\""~EnumName!(A)~"\", ";
	};
	static if (ekv == 2) {
			const char[] BuildEnumElem =
			ToString!(EnumValue!(A, i))~", ";
	}
}

// build the list of key if key is true, the list of values if key is false
template BuildEnumList(char[] A, char[]enumListName, int i, int ekv) {
    static if (SplitChomp!(A, ",").Rest.length == 0) {
        const char[] BuildEnumList =
            BuildEnumElem!(A, enumListName, EnumValue!(A, i), ekv);
    } else {
        const char[] BuildEnumList =
            BuildEnumElem!(SplitChomp!(A, ",").First, enumListName, EnumValue!(A, i), ekv)~
            BuildEnumList!(SplitChomp!(A, ",").Rest, enumListName, EnumValue!(A, i)+1, ekv);
    }
}

//to be mature it should parse also the EnumBaseType
template DefEnum(char[] enum_str) {
    mixin("enum "~enum_str~";");
	
	mixin(
	"static char[] toString("~EnumListName!(enum_str)~" val)
	{
		int x = val; "~
		BuildEnumSwitch!(EnumBody!(enum_str), 0, false)~
	" }"
	);
	
	mixin(
	"static char[] toFullString("~EnumListName!(enum_str)~" val)
	{
		int x = val; "~
		BuildEnumSwitch!(EnumBody!(enum_str), 0, true)~
	" }"
	);

	mixin(
	"const "~EnumListName!(enum_str)~"[] "~EnumListName!(enum_str)~"_elem ="~
	" [ "~
	   BuildEnumList!(EnumBody!(enum_str), EnumListName!(enum_str), 0, 0)~
	" ];"
	);
	
	mixin(
	"const char[][]"~EnumListName!(enum_str)~"_keys = [ "~
	   BuildEnumList!(EnumBody!(enum_str), EnumListName!(enum_str), 0, 1)~
	"  ];"
	);
	
	mixin(
	"const int[]"~EnumListName!(enum_str)~"_values = [ "~
	   BuildEnumList!(EnumBody!(enum_str), EnumListName!(enum_str), 0, 2)~
	"  ];"
	);	
}



int main(char[][] args)
{
//	mixin DefEnum!("LEnum2 {other=2, cont, end2=15, ps}");
    mixin DefEnum!("LEnum {start, middle, end=10, ps, pps,}");

    LEnum s = LEnum.start;
    writefln("s is %s, with name %s full name '%s'\n", s, toString(s), toFullString(s));
	
	foreach(i,v; LEnum_elem) {
        writefln("Enum %d has name=%s value %d", i, toString(v), v);
    } 

//	LEnum2 o = LEnum2.other;
//	writefln("o is %s, with name %s full name '%s'\n", o, toString(o), toFullString(o));
	
    foreach(i,v; LEnum_keys) {
        writefln("Enum %d has name=%s and value %d", i, v, LEnum_values[i]);
    }
	
	foreach(i,v; LEnum_values) {
        writefln("Enum %d has name=%s value %d", i, toString(cast(LEnum)v), v);
    } 
	
    return 0;
} 





More information about the Digitalmars-d mailing list