reflective enums
Kevin Bealer
kevinbealer at gmail.com
Fri Feb 16 09:28:54 PST 2007
== Quote from renoX (renosky at free.fr)'s article
> Kevin Bealer Wrote:
...
> - why do Enum start the values at 1 instead of 0?
>
> IMHO it should follow the way 'enum' works otherwise developers may be confused.
You're right -- this should match the default enum{} behavior. I actually did it
this way as a kind of shorthand for myself. When I write an enum {} I usually add
a starting value with a name like "e_None" or something. You could think of it as
comparable to the floating point NaN. If I don't set an enumerated type, I want
it to have an out-of-bound value.
> - why the name getString instead of the usual toString name?
That could be changed too; I think of toString() as an object method, and
when adding a static method, I figured I should use a different name to
avoid conflicting with the method. I didn't really check whether there
is a real conflict on this.
There is kind of a strangeness with the way I defined this in that you
creating instances of the Enum!(...) struct is not useful. The type is
only really interesting for its static properties.
> - 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.
These modifications make sense. I'm thinking I can write a much smaller and more
straightforward
version with 1.006.
> 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