1

In Java, enums come with several capabilities missing from C/C++ enums. I would like to be able to define enums with, say, some macro ENUM(Color, Red, Green, Blue) or ENUM(Color, {Red, Green, Blue} ) or what-not, and have an enum defined with these capabilities implemented somehow.

Specifically, I would like to be able to do the following:

for (Color p : Color::values()) { }

and

Color color(Red);
cout << "My face shone a bright " << color;

and

Color color = valueOf<Color>("Green");
/* ... */
cout  << "It ain't easy being " << color;

Notes:

  • Bonus points if the solution lets me specify the (integral) value for each enum value (e.g. Red is 2, Green is 5, Blue is 19).
  • Bonus points if the solution lets me specify the 'inherited type', a-la enum Color : unsigned char { Red, Green, Blue};
  • Suggestions using Boost are welcome, although I would rather stick to just the standard library.
  • I'm not 'married' to macros.
  • This question relates to this one, but is not the same. That question is concerned mainly with allowing multiple data fields, while I don't care about that and am satisfied with a proper single-type enum.
Community
  • 1
  • 1
einpoklum
  • 118,144
  • 57
  • 340
  • 684

2 Answers2

1

After some experiments I've got the following.

#define empty
#define secondSyn()         second
#define second(x, y, ...)   y
#define endOfParams()       ,
#define mapSyn()            mapc empty
#define mapc(fun, x, ...)   , secondSyn empty () (x(), fun(x) mapSyn empty () (fun, __VA_ARGS__))
#define map(fun, x, ...)    secondSyn empty () (x(), fun(x) mapSyn empty () (fun, __VA_ARGS__))


#define eval(...)           eval2(eval2(eval2(eval2(eval2(eval2(eval2(eval2(__VA_ARGS__))))))))
#define eval2(...)          eval3(eval3(eval3(eval3(eval3(eval3(eval3(eval3(__VA_ARGS__))))))))
#define eval3(...)          eval4(eval4(eval4(eval4(eval4(eval4(eval4(eval4(__VA_ARGS__))))))))
#define eval4(...)          eval5(eval5(eval5(eval5(eval5(eval5(eval5(eval5(__VA_ARGS__))))))))
#define eval5(...)          __VA_ARGS__


#define IDENTITY(x)         x
#define STRINGIFY(x)        #x

#define DEFENUM(name, ...) \
    enum name { eval(map(IDENTITY, __VA_ARGS__, endOfParams)) }; \
    char *name##Names[] = { eval(map(STRINGIFY, __VA_ARGS__, endOfParams)) }

and

DEFENUM(Color, Red, Green, Blue);

It worked with my gcc. You shall rename majority of macros before real use. For very large enums you will need to add eval6 macro. Hope it helps.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Marian
  • 7,402
  • 2
  • 22
  • 34
  • First of all, +1 for sure :-) Now, can you explain the meaning of the multiple evalN definitions? It is not entirely clear to me what they're doing. More generally, you seem to be performing some arcane magic trick to get over the fact that there aren't recursive `#define`s in C/C++. Can you please elaborate some more on this? – einpoklum Jan 06 '14 at 17:55
  • Well, just to be clear, personally I still prefer another solution with several macros. This solution is very tricky and I believe it will not work with all preprocessor implementations. The rough idea is that "mapc" is performing one step of recursive mapping. "eval" is a simple identity, however each time the macro is invoked, it performs macro expansion of its argument, hence doing one more step of nested (recursive) evaluation. Many nested evals will perform many (recursive) expansions and evaluate expression in sufficient depth. – Marian Jan 06 '14 at 18:42
  • The thing is, you're combining both the nesting of evalN+1's within evalNs, but also nesting calls of evalN's within each other (`eval3(eval3(eval3(...(eval3(__VA_ARGS__))..)`) why are both necessary? – einpoklum Jan 07 '14 at 08:03
  • It is increasing the number of evaluations exponentially. Right now, eval is expanded to 8^4 of eval5 giving 4096 steps of argument evaluations. You can use those macros to define an enumeration up to around 4000 enumerators. To increase this number, you can define `eval5(...)` as `eval6(eval6(eval6(eval6(eval6(eval6(eval6(eval6(__VA_ARGS__))))))))` and `eval6` as `__VA_ARGS__`. It will increase the number of evaluations to 8^5 == 32768. – Marian Jan 07 '14 at 09:58
0

This article is a pretty good analysis. http://www.wambold.com/Martin/writings/typesafe-enums.html

Its value is in using some c++ idioms and approaches rather than a simple naive conversion.

Here's the "final" version (add toString etc):

class Suit
{
public:
  enum private_SUIT_ { CLUBS, DIAMONDS, HEARTS, SPADES };

private:
  typedef private_SUIT_ SUIT_;
  SUIT_ S_;

  template <SUIT_ S> class Literal;

public:
  template <SUIT_ S>
  Suit (const Literal<S>&) : S_ (S) {}

  SUIT_ toEnum () const { return S_; }

  inline friend bool operator== (Suit s, Suit t) { return s.S_ == t.S_; }
  inline friend bool operator!= (Suit s, Suit t) { return ! (s == t); }

  static const Literal<CLUBS>    clubs;
  static const Literal<DIAMONDS> diamonds;
  static const Literal<HEARTS>   hearts;
  static const Literal<SPADES>   spades;
};

template <Suit::private_SUIT_ S>
class Suit::Literal : private Suit
{
public:
  Suit::private_SUIT_ toEnum () const { return S; }
private:
  friend class Suit;
  Literal () : Suit (*this) {}
  // Prevent treating literals as objects
  void* operator new (size_t);      // outlawed
  void  operator delete (void*);    // outlawed
  void  operator= (const Literal&); // outlawed
  void* operator& () const;         // outlawed
};
KarlM
  • 1,614
  • 18
  • 28