reflective enums part 2
Kevin Bealer
kevinbealer at gmail.com
Sat Feb 17 15:29:02 PST 2007
This is the reflective enums stuff rewritten for DMD 1.006. It's not
much shorter than the previous version, but a lot of it is replacements
for basic string stuff that is not 'compile timeable'. The last version
used std.metastrings, but that code is not usable here.
I didn't make these functions very general because I assume that either
libraries of compile-timeable stuff will appear, or (more likely) a few
of the restrictions on what can be done at compile time will disappear,
making std.strings 'just work' at compile time.
Right now it looks like the only allocations CFTE code can do is string
concatenation (I think Walter mentioned this in the announcement).
Kevin
// -*- c++ -*-
import std.stdio;
import std.string;
import std.file;
// For DMD 1.006
// Reusable functions
char[] split_delim(char[] A, char delim, bool second)
{
for(int i = 0; i < A.length; i++) {
if (A[i] == delim) {
return second ? A[i+1..$] : A[0..i];
}
}
return second ? "" : A;
}
char[] trim(char[] A, char delim)
{
while(A.length && A[0] == delim) A = A[1..$];
while(A.length && A[$-1] == delim) A = A[0..$-1];
return A;
}
char[] numberToString(T)(T x)
{
if (x == 0)
return "0";
char[] rv, neg = (x < 0) ? "-" : "";
if (neg.length)
x = -x;
while(x > 0) {
int d = x % 10;
rv = ("0123456789"[d..d+1]) ~ rv;
x = x / 10;
}
return neg ~ rv;
}
int stringToNumber(char[] x)
{
int sign = (x[0] == '-') ? 1 : 0;
long mult = 1, denom = 0;
for(int i = x.length-1; i >= sign; i--) {
assert(x[i] >= '0' && x[i] <= '9');
denom += (x[i] - '0') * mult;
mult *= 10;
}
return sign ? -denom : denom;
}
char[] format2(char[] fmt, char[] f0, char[] f1)
{
char[] rv;
bool slash = false;
foreach(i, ch; fmt) {
if (slash) {
slash = false;
switch(ch) {
case '0': rv ~= f0; break;
case '1': rv ~= f1; break;
default: rv ~= ch;
}
} else {
slash = (ch == '/');
if (! slash)
rv ~= fmt[i..i+1];
}
}
return rv;
}
// Task-specific functions:
char[] format_enum_list(T = int)(char[] fmt, char[] A, T start = 0)
{
char[] code;
T V = start;
while(A.length) {
char[] front = trim(split_delim(A, ',', false), ' ');
char[] ename = trim(split_delim(front, '=', false), ' ');
char[] value = trim(split_delim(front, '=', true), ' ');
if (value.length)
V = stringToNumber(value);
code ~= format2(fmt, ename, numberToString(V));
A = split_delim(A, ',', true);
V = V + 1;
}
return code;
}
struct Enum(char[] A, T = int) {
const char[] def2_ = format_enum_list!(T)("const enum_t /0 =
(/1);\n", A);
const char[] def_ = "typedef "~T.stringof~" enum_t;\n"~def2_;
mixin(def_);
static char[] getString(T x)
{
const char[] cases = format_enum_list!(T)("case /1: return
\"/0\";\n", A);
mixin("switch(x) {\n"~ cases~ "default: break;}");
throw new Exception("enumeration out of range");
}
int opApply(int delegate(inout T) dg)
{
int i_, rv_;
const char[] applies =
format_enum_list!(T)("i_=/1; rv_ = dg(i_); if (rv_) return
rv_;\n", A);
mixin(applies);
return rv_;
}
}
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));
}
alias Enum!("up, down, charmed, strange, top, bottom") Enum2;
Enum2.enum_t eggbert = Enum2.charmed;
// Note: this would be a syntax error because the two
// enums are distinct types and are not interchangeable
// without a cast (intentionally).
//PState.enum_t middle = eggbert;
writefln("%s\n", P.enum_t.stringof);
return 0;
}
More information about the Digitalmars-d
mailing list