Since C99, Keine Lust's macros could even be extended to set values explicitly:
#define X(x) x,
#define X_WITH_VALUE(x, v) x = v,
and:
#define X(x) [x] = #x,
#define X_WITH_VALUE(x, v) X(x)
finally a function:
char const* getName(enum E e)
{
char const* n = e < sizeof(arr)/sizeof(*arr) ? arr[e] : NULL;
return n ? n : "<unknown error>";
}
Edit (in response to comment): Assigning values explicitly allows to introduce
(desired!) gaps and synonyms:
enum Flags
{
None = 0,
F0 = 1,
F1 = 2,
F2 = 4,
F3 = 8,
G3 = F3,
}
This will result in gaps in the array, though, resulting in necessity for the null pointer check in the function. If your enum values get large, the array might get huge, so the array solution might not be suitable any more...
The synonyms impose another problem, see Jens's answer. The problem is solved partially, code does work, but you do not necessarily get back the synonym originally used, but always the last defined one instead! In above example, this would be G3 even if you used F3 in your code. So you have to define the desired synonym last (which seems a little unnatural to me...) or you use the SYNONYM macro below.
Some special cases with array size might be solved with additional tricks, e. g. if you have subsequent values with a higher start value:
#define X(x) [x - Initial] = #x,
char const* n = e < sizeof(arr)/sizeof(*arr) ? arr[e - Initial] : NULL;
Interesting gets the powers of two enum above:
#define X(x, p) x = (1 << p),
enum err_t {None = 0, ERRS};
char const* getName(enum E e)
{
if(e == None)
return S(None); // stringification macro, yet to be defined
// check, if power of two:
if((unsigned int) e & ((unsigned int) e - 1))
{
int index = log2(e);
if(index < sizeof(arr)/sizeof(*arr)
return arr[index];
}
return "<unknown>";
}
The power of two check is coming from Sean Eron Anderson (here exactly), you find nice solutions to calculate log2 there, too, e. g. this one.
Completely different approach, suitable for any kind of distinct values (but synonyms need to be handled explicitly!):
#define X(x) x,
#define X_WITH_VALUE(x, v) x = v,
#define SYNONYM(y, x) y = x,
#define X(x) case x: return #x;
#define X_WITH_VALUE(x, v) X(x)
#define SYNONYM(y, x)
char const* getName(enum E e)
{
switch(e)
{
ERRS
default:
return "<unknown>";
}
}