digitalmars.D - reflective enums part 2
- Kevin Bealer (134/134) Feb 17 2007 This is the reflective enums stuff rewritten for DMD 1.006. It's not
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; }
Feb 17 2007