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