2014-01-06 2 views
1

В Java перечисления имеют несколько возможностей, отсутствующих в перечислениях C/C++. Я хотел бы иметь возможность определять перечисления с, скажем, с помощью макроса ENUM(Color, Red, Green, Blue) или ENUM(Color, {Red, Green, Blue}) или что-то не так, и иметь перечисление, определенное этими возможностями, каким-то образом реализовано.Автоматизация добавления Java-enum-подобных возможностей для перечисления

В частности, я хотел бы быть в состоянии сделать следующее:

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

и

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

и

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

Примечания:

  • Бонусные баллы, если решение позволяет мне указать (интегральное) значение для каждого значения перечисления (например, Красный - 2, зеленый - 5, синий - 19).
  • Бонусные баллы, если решение позволяет мне указать «унаследованный тип», а-ля enum Color : unsigned char { Red, Green, Blue};
  • Предложения с использованием подталкивание приветствуются, хотя я предпочел бы придерживаться только в стандартной библиотеке.
  • Я не «женат» на макросах.
  • Этот вопрос относится к this one, но это не то же самое. Этот вопрос касается главным образом разрешения нескольких полей данных, в то время как я не забочусь об этом и доволен надлежащим перечислением одного типа.

ответ

1

После некоторых экспериментов у меня есть следующее.

#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)) } 

и

DEFENUM(Color, Red, Green, Blue); 

Он работал с моим НКУ. Вы должны переименовать большинство макросов перед реальным использованием. Для очень больших перечислений вам нужно добавить макрос eval6. Надеюсь, поможет.

+0

Прежде всего, +1 наверняка :-) Теперь, вы можете объяснить значение нескольких определений evalN? Мне не совсем понятно, что они делают. В более общем плане вы, кажется, выполняете какой-то магический трюк, чтобы понять, что в C/C++ нет рекурсивных '# define'. Не могли бы вы подробнее рассказать об этом? – einpoklum

+0

Ну, просто чтобы быть ясным, лично я по-прежнему предпочитаю другое решение с несколькими макросами. Это решение очень сложно, и я считаю, что он не будет работать со всеми реализациями препроцессора. Грубая идея заключается в том, что «mapc» выполняет один шаг рекурсивного отображения. «eval» - это простая идентификация, однако каждый раз, когда вызывается макрос, он выполняет макрораспределение своего аргумента, поэтому делает еще один шаг вложенной (рекурсивной) оценки. Многие вложенные оценки будут выполнять множество (рекурсивных) расширений и оценивать выражение на достаточной глубине. – Marian

+0

Дело в том, что вы комбинируете вложение evalN + 1 в evalNs, но также вставляете вызовы evalN внутри друг друга ('eval3 (eval3 (eval3 (... (eval3 (__ VA_ARGS __))))) почему это необходимо? – einpoklum

0

Эта статья представляет собой довольно хороший анализ. http://www.wambold.com/Martin/writings/typesafe-enums.html

Его значение заключается в использовании идиом и подходов C++, а не просто наивного преобразования.

Вот «окончательная» версия (добавить ToString и т.д.):

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 
}; 
Смежные вопросы