reflective enums
Kevin Bealer
kevinbealer at gmail.com
Thu Feb 15 21:28:42 PST 2007
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.
I think once I update to the next version of DMD I should be able to cut
all this down to about 20 lines, but here it is for 1.005.
The Enum template allows you to declare a struct that contains a
definition of constants (like an enumeration) but also allows you to get
the strings for the constants and foreach() over the data.
Note: the lines of code in main() represent the entirety of testing
on this code, so it probably has bugs and issues. Also, checking for
invalid input to the templates was not important to me in this case.
(I'm writing a toy project where I need (as so often happens) both an
enum and a corresponding "enumToString" function. Reading all those
links from the other day about LISP and LISP macros seems to have given
me a temporary allergy to 'repeating myself'. So I figured I would see
if I can solve the enum + string problem -- this is the result.)
On the subject of 'bloat', if I comment out the template definitinos and
the contents of main() below, the 'strip'ed version of the binary trims
by about 1.5 k, which doesn't seem too bad.
234268 // all code shown below
232796 // no code, but imports
174368 // no code or imports
(I don't really care about the 232k -- the C++ binaries I build at work
are something like 130 MB in debug mode; we have a lot of libraries.)
Kevin
// -*- c++ -*-
import std.stdio;
import std.metastrings;
import std.string;
import std.file;
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!(SplitChomp!(A, ",").First, "=").First EnumName;
}
template EnumAssign(char[] A) {
alias SplitChomp!(SplitChomp!(A, ",").First, "=").Rest EnumAssign;
}
template EnumValue(char[] A, int next) {
static if (EnumAssign!(A) == "") {
const int EnumValue = next;
} 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 BuildOneCase(char[] A, int i) {
const char[] BuildOneCase =
"case "~ToString!(EnumValue!(A, i))~
": return \""~EnumName!(A)~"\";\n";
}
template BuildOneApply(char[] A, int i) {
const char[] BuildOneApply =
"i="~ToString!(EnumValue!(A, i))~";"~
" rv=dg(i); if (rv) return rv;";
}
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 BuildEnumCases(char[] A, int i) {
static if (SplitChomp!(A, ",").Rest.length == 0) {
const char[] BuildEnumCases =
BuildOneCase!(A, EnumValue!(A, i));
} else {
const char[] BuildEnumCases =
BuildOneCase!(SplitChomp!(A, ",").First,
EnumValue!(A, i)) ~
BuildEnumCases!(SplitChomp!(A, ",").Rest,
EnumValue!(A, i)+1);
}
}
template BuildEnumSwitch(char[] var, char[] A, int i) {
const char[] BuildEnumSwitch =
"switch("~var~") {"~
BuildEnumCases!(A, i) ~
"default: "~
" throw new Exception(\"enumeration out of range\");"
"}";
}
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, 1));
static char[] getString(int x)
{
mixin(BuildEnumSwitch!("x", A, 1));
}
int opApply(int delegate(inout int) dg)
{
int i, rv;
mixin(BuildEnumApply!(A, 1));
return 0;
}
}
int main(char[][] args)
{
alias Enum!("start, middle, end=10, ps, pps") PState;
int p = PState.middle;
writefln("p is %s, with name %s\n", p, PState.getString(p));
PState P;
foreach(v; P) {
writefln("Enum %s has name=%s", v, PState.getString(v));
}
return 0;
}
More information about the Digitalmars-d
mailing list