You could use the preprocessor to do this, I believe this technique is called X-Macros:
/* fruits.def */
X(APPLE)
X(MANGO)
X(ORANGE)
/* file.c */
enum fruits {
#define X(a) a,
#include "fruits.def"
#undef X
};
const char *fruit_name[] = {
#define X(a) #a,
#include "fruits.def"
#undef X
};
Note that the last entry includes a trailing comma, which is allowed in C99 (but not in C89). If that is a problem you can add sentinal values. It is also possible to make the macro more complicated by giving multiple arguments for custom names or enum values, etc:
X(APPLE, Apple, 2)
#define X(a,b,c) a = c, /* in enum */
#define X(a,b,c) [c] = #b, /* in name array */
Limitations: You cannot have negative constants and your array is sizeof (char *) * largest_constant
. But you could work around both by using an extra lookup table:
int map[] = {
#define X(a,b,c) c,
#include "fruits.def"
#undef X
};
This doesn't work of course. What does work is generating an extra set of enum
constants as keys for the names:
enum fruits {
#define X(a,b,c) a ## _KEY,
#include "fruits.def"
#undef X
#define X(a,b,c) a = c,
#include "fruits.def"
#undef X
};
Now you can find the name of X(PINEAPPLE, Pineapple, -40)
by using fruit_name[PINEAPPLE_KEY]
.
People noted that they didn't like the extra include file. You don't need this extra file, you also use a #define
. This may be more appropriate for small lists:
#define FRUIT_LIST X(APPLE) X(ORANGE)
And replace #include "fruits.def
with FRUIT_LIST
in the previous examples.