Можно ли определить enumalpha? - PullRequest
1 голос
/ 03 апреля 2010

Я бы хотел написать:

cout << enumalpha << Monday;

и напечатайте на консоли:

понедельник

P.S. Понедельник - тип enum.

Ответы [ 3 ]

8 голосов
/ 03 апреля 2010

Хорошо, тогда пойдем все препроцессоры:)

Предполагаемый способ использования:

DEFINE_ENUM(DayOfWeek, (Monday)(Tuesday)(Wednesday)
                       (Thursday)(Friday)(Saturday)(Sunday))

int main(int argc, char* argv[])
{
  DayOfWeek_t i = DayOfWeek::Monday;
  std::cout << i << std::endl;            // prints Monday

  std::cin >> i >> std::endl;             // reads the content of a string and
                                          // deduce the corresponding enum value
}

Темная магия, включающая полезную библиотеку Boost.Preprocessor .

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#define DEFINE_ENUM_VAL_TO_STR(r, data, elem)                      \
   case BOOST_PP_CAT(data, BOOST_PP_CAT(::, elem)):                \
    return out << BOOST_PP_STRINGIZE(elem);

#define DEFINE_ENUM_STR_TO_VAL(r, data, elem)                      \
   if (s == BOOST_PP_STRINGIZE(elem))                              \
   { i = BOOST_PP_CAT(data, BOOST_PP_CAT(::, elem)) ; } else


#define DEFINE_ENUM(Name, Values)                                  \
   struct Name {                                                   \
     enum type {                                                   \
       Invalid = 0,                                                \
       BOOST_PP_SEQ_ENUM(Values)                                   \
     };                                                            \
   };                                                              \
   typedef Name::type Name##_t;                                    \
   std::ostream& operator<<(std::ostream& out, Name##_t i) {       \
    switch(i) {                                                    \
      BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_VAL_TO_STR, Name, Values)  \
    default: return out << "~"; } }                                \
   std::istream& operator>>(std::istream& in, Name##_t& i) {       \
     std::string s; in >> s;                                       \
     BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_STR_TO_VAL, Name, Values)   \
     { i = Name##::Invalid; } }

Есть лучшие способы, лично я использую этот маленький макрос, чтобы хранить все это в хорошо отсортированном векторе пар, статичных для типа, он также позволяет мне перебирать значения перечисления, если настроение (или необходимость) поражает :)

Это весьма прискорбно, хотя в этом языке нет поддержки. Я бы предпочел, если бы это было, enum довольно удобен для кодовых наборов ...

4 голосов
/ 03 апреля 2010

Точная форма того, что вы хотите, насколько я знаю, невозможна. Как сказал Нил, имена для нас просто люди; компилятор имеет дело со значениями.

Тем не менее, вы можете создать утилиту для присвоения имен перечислениям. Вот пример:

#define ENUM_NAMES_BEGIN(pType) \
    std::ostream& operator<<(std::ostream& pStream, pType pValue) \
            { \
                switch (pValue) \
                {

#define ENUM_NAMES_CASE_NAMED(pValue, pName) \
                case (pValue): \
                    pStream << (pName); \
                    break;

#define ENUM_NAMES_CASE(pValue) ENUM_NAMES_CASE_NAMED(pValue, #pValue)

#define ENUM_NAMES_END(pDefault) \
                default: \
                    pStream << (pDefault); \
                } \
                \
                return pStream; \
            }

Вы бы использовали его так:

#include <iostream>

enum Days
{
    Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
};

enum NotNamed
{
    DontTry, ImNotnamed
};

ENUM_NAMES_BEGIN(Days)
    ENUM_NAMES_CASE(Sunday)
    ENUM_NAMES_CASE(Monday)
    ENUM_NAMES_CASE(Tuesday)
    ENUM_NAMES_CASE(Wednesday)
    ENUM_NAMES_CASE(Thursday)
    ENUM_NAMES_CASE(Friday)
    ENUM_NAMES_CASE_NAMED(Saturday, "Saturday: Fun day!")
ENUM_NAMES_END("")

int main()
{
    Days d = Saturday; // or whatever
    NotNamed n = ImNotnamed;

    std::cout << "Day: " << d << std::endl;
    std::cout << "Not Named: " << n << std::endl;
}

Попытка его с типом, который "безымянный", возвращает его числовое значение.

Обратите внимание, что здесь нет никакого перечисления enum; Вы можете использовать это, например, для именования целочисленных значений. Если бы вы это сделали, operator<< было бы неоднозначно.

Если вы можете использовать Boost, используйте черту их типа is_enum (что довольно сложно) и статически утверждают, что так оно и есть. Для этого изменения будут:

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_enum.hpp>

#define ENUM_NAMES_BEGIN(pType) \
    std::ostream& operator<<(std::ostream& pStream, pType pValue) \
            { \
                BOOST_STATIC_ASSERT(boost::is_enum<pType>::value); \
                switch (pValue) \
                {

Теперь, если тип не является перечислением, ошибка компиляции, по крайней мере, указывает на строку, где вы пытаетесь определить имена перечисления.

2 голосов
/ 03 апреля 2010

(нет, это не отвечает на вопрос для общего случая, но может все же быть интересным для кого-то)

Как Нил говорит в комментариях к этому вопросу, вы не можете сделать это в общем случае для перечислений. Однако для отдельных случаев вы можете перегрузить оператор вставки потока (<<) для перечислимого типа:

#include <iostream>

enum day_of_week
{
    friday,
    saturday,
    sunday
};

std::ostream& operator<<(std::ostream& o, day_of_week day)
{
    switch (day)
    {
    case friday:   o << "Friday";   break;
    case saturday: o << "Saturday"; break;
    case sunday:   o << "Sunday";   break;
    }
    return o;
}

Например, с приведенным выше кодом, это:

std::cout << saturday << std::endl;

напечатает:

Saturday
...