Jump to page: 1 2
Thread overview
reflective enums
Feb 16, 2007
Kevin Bealer
Feb 16, 2007
Derek Parnell
Feb 16, 2007
renoX
Feb 16, 2007
Kevin Bealer
Feb 16, 2007
renoX
Feb 17, 2007
janderson
Feb 17, 2007
janderson
Feb 17, 2007
renoX
Feb 17, 2007
Kevin Bealer
Feb 16, 2007
janderson
Feb 16, 2007
Kevin Bealer
February 16, 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;
}
February 16, 2007
On Fri, 16 Feb 2007 00:28:42 -0500, Kevin Bealer wrote:


> The Enum template ...

Now we're talking ... I wonder what 1.006 can do <G>

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Justice for David Hicks!"
16/02/2007 4:34:51 PM
February 16, 2007
Derek Parnell wrote:
> On Fri, 16 Feb 2007 00:28:42 -0500, Kevin Bealer wrote:
> 
>  
>> The Enum template ...
> 
> Now we're talking ... I wonder what 1.006 can do <G>

Probably something along the lines:

mixin ReflectiveEnum!(int, "a", "b", "c");

That generates int-based enums (enumerated values of arbitrary type will be possible, but hey, one thing at a time), along with parsing and printing code.

So it's not about what features are directly useful, but instead the power is in the proverbial level of indirection. I can imagine lots of people could care less about esoteric compile-time stuff, but they'd be glad to use a better enum.


Andrei
February 16, 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;
}








February 16, 2007
Kevin Bealer wrote:
> 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;
> }

Nice one man!

-Joel
February 16, 2007
== Quote from janderson (askme@me.com)'s article
> Kevin Bealer wrote:
> > 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;
> > }
> Nice one man!
> -Joel

Thanks!  I should say as a formality, I release all this into the public domain etc.

Kevin
February 16, 2007
== Quote from renoX (renosky@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;
> }

February 16, 2007
Kevin Bealer a écrit :
> == Quote from renoX (renosky@free.fr)'s article
>> - 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.

From my testing, it doesn't trigger a conflict, but I've only tested it inside one file.

> 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.

About this I was wondering if it wouldn't be less strange to do a template like this:

// expected usage: DefEnum!("enum ListEnumFoo {A,B=1};")
template DefEnum(char[] def_enum) {

	mixin(def_enum);

	static toString(EnumType!(def_enum) x)
	{
		// function body similar to the inital get_String()
	}
}

Advantages:
-The reflective enum type is really an enum type, so it acts like one.
-No weird struct.
-When(If) I can convince Walther that writef("foo %s",x) means really writef("foo "~x.toString()); we could write:
writef("enum x value is %d and name is %s\n",x,x); and have the correct result, because toString(ListEnumFoo x) would hide toString(int x) {x being an enum variable from type ListEnumFoo).

Disadvantage:
No easy way to iterate among the enum values: we cannot do
foreach(v; ListEnumFoo) because now ListEnumFoo is an enum not a struct..
And we cannot pass an enum type as a parameter, other it would be easy to define function 'keys' and 'values' (like for associative arrays), so we'd need a dummy parameter, i.e you wouldn't be able to write
foreach (v; ListEnumFoo) {} and
neither foreach (v; ListEnumFoo.values()) {}
but foreach (v; ListEnumFoo.A.values()) {} could perhaps work.

What do you think about this other way to do it?

Regards,
renoX
February 17, 2007
renoX wrote:
> Kevin Bealer a écrit :
>> == Quote from renoX (renosky@free.fr)'s article
>>> - 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.
> 
>  From my testing, it doesn't trigger a conflict, but I've only tested it inside one file.
> 
>> 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.
> 
> About this I was wondering if it wouldn't be less strange to do a template like this:
> 
> // expected usage: DefEnum!("enum ListEnumFoo {A,B=1};")

Advantage: you would be able to reuse the code as a sub-set of something else like serialization or another language that uses the same enum syntax.

Disadvantage: Code is harder to understand.  Code runs slower.

-Joel

-Joel
February 17, 2007
janderson wrote:
> renoX wrote:
>> Kevin Bealer a écrit :
>>> == Quote from renoX (renosky@free.fr)'s article
>>>> - 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.
>>
>>  From my testing, it doesn't trigger a conflict, but I've only tested it inside one file.
>>
>>> 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.
>>
>> About this I was wondering if it wouldn't be less strange to do a template like this:
>>
>> // expected usage: DefEnum!("enum ListEnumFoo {A,B=1};")
> 
> Advantage: you would be able to reuse the code as a sub-set of something else like serialization or another language that uses the same enum syntax.
> 
> Disadvantage: Code is harder to understand.  Code runs slower.

Correction: The interpreter code is harder to understand.

> 
> -Joel
> 
> -Joel
« First   ‹ Prev
1 2