tags:

views:

805

answers:

4

What is the best approach to define additional data for typedef enums in C?

Example:

typedef enum {
  kVizsla = 0,
  kTerrier = 3,
  kYellowLab = 10
} DogType;

Now I would like to define names for each, for example kVizsla should be "vizsla". I currently use a function that returns a srting using a large switch block.

A: 

That's kind of an open ended question, but one suggestion would be to use a map with the enum as the key type and the extra information in the value. (If your indices are continuous, unlike the example, you can use a sequence container instead of a map).

On Freund
+1  A: 

If your enumerated values are dense enough, you can define an array to hold the strings and just look them up (use NULL for any skipped value and add a special case handler on your lookup routine).

char *DogList[] = {
  "vizsla", /* element 0 */
  NULL,
  NULL,
  NULL,
  "terrier", /* element 3 */
  ...
};

This is inefficient for sparse enumerations.

Even if the enumeration is not dense, you can use an array of structs to hold the mapping.

typedef struct DogMaps {
  DogType index;
  char * name;
} DogMapt;
DogMapt DogMap[] = {
  {kVizsla, "vizsla"},
  {kTerrier, "terrier"},
  {kYellowLab, "yellow lab"},
  NULL
};

The second approach is very flexible, but it does mean a search through the mapping every time you need to use the data. For large data sets consider a b-tree or hash instead of an array.

Either method can be generalized to connect more data. In the first use an array of structs, in the second just add more members to the struct.

You will, of course, want to write various handlers to simplify your interaction with these data structures.


@Hershi By all means, separate code and data. The above examples are meant to be clear rather than functional.

I blush to admit that I still use whitespace separated flat files for that purpose, rather than the kind of structured input you exhibit, but my production code would read as much of the data from external sources as possible.


Wait, I see that you mean code generation.

Sure. Nothing wrong with that.

I suspect, though that the OP was interested in what the generated code should look like...

dmckee
+2  A: 

@dmckee: I think the suggested solution is good, but for simple data (e.g. if only the name is needed) it could be augmented with auto-generated code. While there are lots of ways to auto-generate code, for something as simple as this I believe you could write a simple XSLT that takes in an XML representation of the enum and outputs the code file.

The XML would be of the form:

<EnumsDefinition>
    <Enum name="DogType">
        <Value name="Vizsla" value="0" />
        <Value name="Terrier" value="3" />
        <Value name="YellowLab" value="10" />
    </Enum>
</EnumsDefinition>

and the resulting code would be something similar to what dmckee suggested in his solution.

For information of how to write such an XSLT try here or just search it up in google and find a tutorial that fits. Writing XSLT is not much fun IMO, but it's not that bad either, at least for relatively simple tasks such as these.

Hershi
+2  A: 

A perfect fit for X() macros. These types of macros can use the C preprocessor to construct enums and arrays from the same source. You only need to add new data to the #define containing the X() macro.

Your example can be written as follows:

// All dog data goes in this list
#define XDOGTYPE \
  X(kVizsla,0,"vizsla") \
  X(kTerrier,3,"terrier") \
  X(kYellowLab,10,"yellowlab")

 // Dog info
 typedef struct {
     int val;       // Defined value
     char * desc;   // Text description
 } DogType;

 // Build an array index using the Names
 typedef enum {
  #define X(Name,Val,Text)     Name,
   XDOGTYPE
  #undef X
  MAXDOGS
 } DogIndex;

 // Build a lookup table of values
 DogType Dog[] = {
  #define X(Name,Val,Text)    {Val,Text},
   XDOGTYPE
  #undef X
 };

 // Access the values
 for (i=0; i < MAXDOGS; i++)
    printf("%d: %s\n",Dog[i].val,Dog[i].desc);