Design of reflective enums
Kevin Bealer
kevinbealer at gmail.com
Mon Feb 19 15:04:12 PST 2007
renoX wrote:
> Hello,
> Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible.
> Reflective enums have two useful properties that don't have normal enums:
> - their label is printable
> - it's possible to do a foreach on all the possible values.
>
> In my opinion, for the reflective enums, the following constraint should apply:
> - they should 'look like' normal enums as much as possible.
> - then the printable label feature should be as simple to use as possible: this feature will probably be used much more often than the foreach feature.
>
> For the API, the functions needed are:
> a- Reflective Enum(REnum) list definition.
> b. REnum variable definition.
> c. Reference to an REnum label.
> d. Print the label corresponding to the value of an REnum variable.
> e. Iterate over all possible value in an REnum list.
> f. Iterate over all possible labels in an REnum list (I've added this one by symmetry).
>
> (a) Takes a name and either define (1) an enum of this name plus some functions to print enum labels and iterate over it, or alternatively as in the original code it can return a 'thing' (struct for example as it in the initial implementation) (2) which contain the enum and functions.
> I'm not sure which is best.
>
> (e) and (f) reminds me of associate arrays, maybe a function to generate an associative array from the enum list would be enough..
>
> I'm trying to create functions to (e) and (f), but I have trouble to make my code work currently: debugging template code is not very easy..
>
> renoX
>
Your comment about e and f matches something I've been thinking. The
action of iterating over the enum and in fact looking up the string are
essentially similar to an associative array. Imagine these two
definitions in regular D:
enum Xyz_t {
x = 10,
y = 100,
z = 201
};
char[][Xyz_t] Xyz_t_lookup;
lookup[x] = "x";
lookup[y] = "y";
lookup[z] = "z";
Xyz_t[] Xyz_t_keys = Xyz_t_lookup.keys;
char[][] Xyz_t_labels = ["x", "y", "z"];
Now, I can do all the above operations with these definitions. We could
build a reflective enum that converted to this definition. The main
thing that I would change is that instead of a regular associative
array, we can define a compile-time associative array. We can be more
efficient in a CTAA than an AA because all values are known at compile
time -- we convert all lookups to switches. It would look like this:
// given types T1 and T2
// constructed like this:
// CTAA!(int, char[], "10, \"x\", 100, \"y\", 201, \"z\"");
struct CTAA(T1, T2, E... values) {
const T1[] keys = [10,100,201];
const T2[] values = ["x","y","z"];
int opApply(int delegate(T1 k, T2 label) dg)
{
int rv;
rv = dg(10, "x"); if (rv) return rv;
rv = dg(100, "y"); if (rv) return rv;
rv = dg(201, "z"); if (rv) return rv;
return 0;
}
T1 getKey(T2 label)
{
switch(label) {
case "x": return 10;
case "y": return 100;
case "z": return 201;
default: throw new Exception("unknown label");
}
}
T2 getLabel(T1 key)
{
switch(label) {
case 10: return "x";
case 100: return "y";
case 201: return "z";
default: throw new Exception("unknown enum");
}
}
}
Note that the "\"x\"" stuff is a bit awkward but the reflective enum
would be a wrapper around the AA; since it knows that it's 'strings' are
just C identifiers, it can omit the escaped quotes in its initializer.
Also, since the CTAA is not a real AA there is no reason not to provide
the translation in both directions as shown above -- it's not really an
AA just a quick way to define a couple of handy switch statements.
In principle, switch/case should be more or less always be faster than
actual AAs.
Kevin
More information about the Digitalmars-d
mailing list