Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C/C++: any way to get reflective enums?

I've encountered this situation so many times...

 enum Fruit {
  Apple,
  Banana,
  Pear,
  Tomato
 };

Now I have Fruit f; // banana and I want to go from f to the string "Banana"; or I have string s = "Banana" and from that I want to go to Banana // enum value or int.

So far I've been doing this.. Assuming the enum is in Fruit.h:

// Fruit.cpp
const char *Fruits[] = {
 "Apple",
 "Banana",
 "Pear",
 "Tomato",
 NULL
};

Obviously that's a messy solution. If a developer adds a new fruit to the header and doesn't add a new entry in Fruits[] (can't blame him, they have to be in two different files!) the application goes boom.

Is there a simple way to do what I want, where everything is in one file? Preprocessor hacks, alien magic, anything..

PS: This, contrary to reflection "for everything", would be really trivial to implement in compilers. Seeing how common a problem it is (at least for me) I really can't believe there is no reflective enum Fruit.. Not even in C++0x.

PS2: I'm using C++ but I tagged this question as C as well because C has the same problem. If your solution includes C++ only things, that's ok for me.

like image 882
Thomas Bonini Avatar asked Nov 26 '09 03:11

Thomas Bonini


2 Answers

This one requires the fruits to be defined in an external file. This would be the content of fruit.cpp:

#define FRUIT(name) name
enum Fruit {
#include "fruit-defs.h"
NUM_FRUITS
};
#undef FRUIT
#define FRUIT(name) #name
const char *Fruits [] = {
#include "fruit-defs.h"
NULL
};
#undef FRUIT

And this would be fruit-defs.h:

FRUIT(Banana),
FRUIT(Apple),
FRUIT(Pear),
FRUIT(Tomato),

It works as long as the values start in 0 and are consecutive...

Update: mix this solution with the one from Richard Pennington using C99 if you need non-consecutive values. Ie, something like:

// This would be in fruit-defs.h
FRUIT(Banana, 7)
...
// This one for the enum
#define FRUIT(name, number) name = number
....
// This one for the char *[]
#define FRUIT(name, number) [number] = #name
like image 57
Gonzalo Avatar answered Oct 17 '22 16:10

Gonzalo


A c99 way that I've found helps reduce mistakes:

enum Fruit {
  APPLE,
  BANANA
};
const char* Fruits[] = {
 [APPLE] = "APPLE",
 [BANANA] = "BANANA"
};

You can add enums, even in the middle, and not break old definitions. You can still get NULL strings for values you forget, of course.

like image 10
Richard Pennington Avatar answered Oct 17 '22 16:10

Richard Pennington