Как получить имя элемента перечисления из его значения

Я объявил тип enum следующим образом:

enum WeekEnum
{
Mon = 0;
Tue = 1;
Wed = 2;
Thu = 3;
Fri = 4;
Sat = 5;
Sun = 6;
};

Как я могу получить название элемента «Пн, Вт и т. д.», если у меня уже есть значение элемента «0, 1 и т. д.»

У меня уже есть такая функция

Log(Today is "2", enjoy! );

И теперь я хочу, чтобы результат был ниже

Сегодня среда, наслаждайтесь


person Nano HE    schedule 30.07.2012    source источник
comment
Возможный дубликат C++: распечатать значение перечисления как текст   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 11.12.2016


Ответы (10)


Вы не можете напрямую, enum в C++ не похожи на перечисления Java.

Обычный подход — создать файл std::map<WeekEnum,std::string>.

std::map<WeekEnum,std::string> m;
m[Mon] = "Monday";
//...
m[Sun] = "Sunday";
person Luchian Grigore    schedule 30.07.2012
comment
Я бы, вероятно, использовал массив перед картой. - person Mooing Duck; 30.07.2012
comment
Также подойдет std::unordered_map (хеш-таблица C++ 11). Предлагаемый массив @MooingDuck будет лучше для производительности из-за причин локальности/кеширования, если у вас нет более ~ 100 типов перечислений. - person Darwyn; 18.08.2017
comment
@Darwyn: массив безусловно лучше, если вы зарезервируете правильный размер. Это тривиальная операция индекса для чтения или записи, и никогда не вставляется и не удаляется. - person Mooing Duck; 18.08.2017
comment
@MooingDuck Операция поиска в хеш-таблице — это O (1), а операция поиска в массиве — O (N). Это означает, что в конечном итоге хэш-таблица будет превосходить (с точки зрения процессорного времени) по мере увеличения N. Эта запись в блоге подводит итог эмпирическим данным: scottmeyers.blogspot.de/2015/09/ - person Darwyn; 19.08.2017
comment
@Darwyn: поиск в хеш-таблице - O (1), но у него есть большая константа для хэш-уравнения. Операция поиска в массиве представляет собой буквально прямую индексацию, без поиска или уравнения. m[Mon] обрабатывается компилятором как m[1], который обычно представляет собой один код операции. Вы думаете о поиске в массиве по значению, а не по индексу. - person Mooing Duck; 19.08.2017
comment
@MooingDuck Вы правы, я ошибочно подумал о поиске по значению, а не по индексу. Спасибо. - person Darwyn; 19.08.2017
comment
Использование массива, вероятно, также подразумевает, что значения перечисления являются смежными. Несмотря на то, что большинство перечислений, объявленных на практике, вероятно, являются смежными, в языке нет ничего, что предписывало бы это. - person saxbophone; 20.04.2019

Вот еще один изящный трюк для определения enum с помощью X Macro:

#include <iostream>

#define WEEK_DAYS \
X(MON, "Monday", true) \
X(TUE, "Tuesday", true) \
X(WED, "Wednesday", true) \
X(THU, "Thursday", true) \
X(FRI, "Friday", true) \
X(SAT, "Saturday", false) \
X(SUN, "Sunday", false)

#define X(day, name, workday) day,
enum WeekDay : size_t
{
    WEEK_DAYS
};
#undef X

#define X(day, name, workday) name,
char const *weekday_name[] =
{
    WEEK_DAYS
};
#undef X

#define X(day, name, workday) workday,
bool weekday_workday[]
{
    WEEK_DAYS
};
#undef X

int main()
{
    std::cout << "Enum value: " << WeekDay::THU << std::endl;
    std::cout << "Name string: " << weekday_name[WeekDay::THU] << std::endl;
    std::cout << std::boolalpha << "Work day: " << weekday_workday[WeekDay::THU] << std::endl;

    WeekDay wd = SUN;
    std::cout << "Enum value: " << wd << std::endl;
    std::cout << "Name string: " << weekday_name[wd] << std::endl;
    std::cout << std::boolalpha << "Work day: " << weekday_workday[wd] << std::endl;

    return 0;
}

Живая демонстрация: https://ideone.com/bPAVTM

Выходы:

Enum value: 3
Name string: Thursday
Work day: true
Enum value: 6
Name string: Sunday
Work day: false
person Killzone Kid    schedule 01.04.2018
comment
Спасибо за упоминание этого подхода. TIL Макросы X - person jacobq; 14.09.2019
comment
В итоге я использовал этот трюк для перечисления в общем заголовке. Однако, поскольку он был общим, мне пришлось преобразовать массив во встроенную функцию, которая возвращает строку, если значение совпадает. (как если бы параметр == имя возврата дня;). Этот подход настолько уродлив, насколько это возможно, но ремонтопригодность на высшем уровне. - person Richard; 20.12.2020

Нет, у вас нет возможности получить "имя" из значения в C++, потому что все символы отбрасываются во время компиляции.

Вам может понадобиться этот способ >X макросов

person RolandXu    schedule 30.07.2012

Вы можете определить оператор, который выполняет вывод.

std::ostream& operator<<(std::ostream& lhs, WeekEnum e) {
    switch(e) {
    case Monday: lhs << "Monday"; break;
    .. etc
    }
    return lhs;
}
person Puppy    schedule 30.07.2012
comment
выдает мне эту ошибку msdn. microsoft.com/query/ - person Brandon; 18.12.2016
comment
Брэндон, если вы получили эту ошибку в MSVC, то причина в том, что у меня были совершенно правильные программы, скомпилированные в дорогих компиляторах, таких как компилятор Intel, с треском проваливающимися в Visual Studio. Это приведет к ошибке в чем-то вроде массива с константой #define в качестве индекса, который будет равен 64 - заявите, что вы не можете использовать это в качестве индекса. Это было целое число, а не число с плавающей запятой, логическое значение или что-то еще. Но он отказался работать. У меня были некоторые из тех же самых проектов, построенных в GCC, Mingw, Intel и более старых компиляторах, таких как Turbo C++, и все же MSVC не знает, что он делает, когда дело доходит до перечислений или констант. - person Seth D. Fulmer; 27.06.2020

Перечисление — это что-то вроде обратного массива. Я считаю, что вы хотите это:

const char * Week[] = { "", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };  // The blank string at the beginning is so that Sunday is 1 instead of 0.
cout << "Today is " << Week[2] << ", enjoy!";  // Or whatever you'de like to do with it.
person Cosine    schedule 30.07.2012
comment
перечисление и массив - две большие разницы. - person Master Yoda; 01.06.2016
comment
Это правильно. В то время как перечисление сопоставляет (исходные) строки с числами, массив может сопоставлять числа со строками. (Видите взаимосвязь?) Титул ОП не может быть достигнут напрямую, но ее основная проблема может быть достигнута. Поскольку в ее примере имена упорядочены по порядку, она может использовать массив, а не std::map (что тяжелее). Следовательно, массив может быть именно тем, что нужно OP, несмотря на то, что он не отвечает на ее заглавный вопрос. - person Cosine; 02.06.2016
comment
Я проголосовал за это, потому что это бесполезно, если значение перечисления и индекс массива не совпадают - person Elvis Dukaj; 14.04.2020

Как я могу получить название элемента «Пн, Вт и т. д.», если у меня уже есть значение элемента «0, 1 и т. д.»

В каком-то старом коде C (некоторое время назад) я нашел аналогичный код:

std::string weekEnumToStr(int n)
{
   std::string s("unknown");
   switch (n)
   {
   case 0: { s = "Mon"; } break;
   case 1: { s = "Tue"; } break;
   case 2: { s = "Wed"; } break;
   case 3: { s = "Thu"; } break;
   case 4: { s = "Fri"; } break;
   case 5: { s = "Sat"; } break;
   case 6: { s = "Sun"; } break;
   }
   return s;
}

Против: это устанавливает «патологическую зависимость» между значениями перечисления и функцией... это означает, что если вы измените перечисление, вы должны изменить функцию, чтобы она соответствовала. Я полагаю, это верно даже для std::map.

Я смутно припоминаю, что мы нашли утилиту для генерации кода функции из кода перечисления. Длина таблицы enum выросла до нескольких сотен... и в какой-то момент, возможно, будет разумным выбором писать код для написания кода.


Примечание -

в попытке улучшить встроенную систему моя команда заменила множество таблиц (более 100?) строк с завершающим нулем, используемых для сопоставления значений enum int с их текстовыми строками.

Проблема с таблицами заключалась в том, что значение вне диапазона часто не замечалось, потому что многие из этих таблиц были собраны в одну область кода/памяти, так что значение вне диапазона выходило за именованный конец(и) таблицы и вернул строку с завершающим нулем из некоторой последующей таблицы.

Использование оператора function-with-switch также позволило нам добавить утверждение в предложение по умолчанию переключателя. Утверждения обнаружили еще несколько ошибок кодирования во время тестирования, и наши утверждения были привязаны к системному журналу static-ram-system-log, который наши полевые специалисты могли искать.

person 2785528    schedule 14.01.2018
comment
Утилита запускалась во время компиляции ... файл make принудительно задавал зависимости файлов. - person 2785528; 15.01.2018

Я добился отличного успеха с техникой, напоминающей макросы X, на которые указывает @RolandXu. Мы также активно использовали оператор stringize. Этот метод избавляет от кошмара обслуживания, когда у вас есть домен приложения, в котором элементы отображаются как в виде строк, так и в виде числовых токенов.

Это особенно удобно, когда доступна машиночитаемая документация, так что строки макроса X(...) могут быть сгенерированы автоматически. Новая документация немедленно привела бы к согласованному обновлению программы, охватывающему строки, перечисления и словари, переводящиеся между ними в обоих направлениях. (Мы имели дело с токенами PCL6).

И хотя код препроцессора выглядит довольно уродливо, все эти технические детали могут быть скрыты в заголовочных файлах, которые больше никогда не придется трогать, как и исходные файлы. Все типобезопасно. Единственное, что меняется, это текстовый файл, содержащий все строки X(...) и, возможно, сгенерированный автоматически.

person Peter - Reinstate Monica    schedule 14.03.2014
comment
Интересная проза об этом подходе на основе макросов, но как насчет кода? - person Neil Justice; 27.01.2016
comment
@NeilJustice Вы читали о макросах X? Это примерно то, что мы сделали в C++ с картой и вектором, позволяющим переходить туда и обратно между перечислениями, строковыми представлениями и значениями токенов. - person Peter - Reinstate Monica; 27.01.2016

Если вы знаете, что фактические метки перечисления соотносятся с их значениями, вы можете использовать контейнеры и std::string_view в C++17 для быстрого доступа к значениям и их строковым представлениям с помощью оператора [ ], отслеживая их самостоятельно. std::string_view будет выделять память только при создании. Они также могут быть обозначены static constexpr, если вы хотите, чтобы они были доступны во время выполнения для большей экономии производительности. Это маленькое консольное приложение должно быть довольно быстрым.

#include <iostream>
#include <string_view>
#include <tuple>    
int main() {
    enum class Weekdays { //default behavior starts at 0 and iterates by 1 per entry
        Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
    };

    static constexpr std::string_view Monday    = "Monday";
    static constexpr std::string_view Tuesday   = "Tuesday";
    static constexpr std::string_view Wednesday = "Wednesday";
    static constexpr std::string_view Thursday  = "Thursday";
    static constexpr std::string_view Friday    = "Friday";
    static constexpr std::string_view Saturday  = "Saturday";
    static constexpr std::string_view Sunday    = "Sunday";
    static constexpr std::string_view opener    = "enum[";
    static constexpr std::string_view closer    = "] is ";
    static constexpr std::string_view semi      = ":";

    std::pair<Weekdays, std::string_view> Weekdays_List[] = {
        std::make_pair(Weekdays::Monday,    Monday),
        std::make_pair(Weekdays::Tuesday,   Tuesday),
        std::make_pair(Weekdays::Wednesday, Wednesday),
        std::make_pair(Weekdays::Thursday,  Thursday),
        std::make_pair(Weekdays::Friday,    Friday),
        std::make_pair(Weekdays::Saturday,  Saturday),
        std::make_pair(Weekdays::Sunday,    Sunday)
    };

    for (int i=0;i<sizeof(Weekdays_List)/sizeof(Weekdays_List[0]);i++) {
        std::cout<<opener<<i<<closer<<Weekdays_List[(int)i].second<<semi\
        <<(int)Weekdays_List[(int)i].first<<std::endl;
    }    
    return 0;
}

Вывод:

enum[0] is Monday:0
enum[1] is Tuesday:1
enum[2] is Wednesday:2
enum[3] is Thursday:3
enum[4] is Friday:4
enum[5] is Saturday:5
enum[6] is Sunday:6
person kayleeFrye_onDeck    schedule 03.11.2018

В GCC это может выглядеть так:

const char* WeekEnumNames [] = {
    [Mon] = "Mon",
    [Tue] = "Tue",
    [Wed] = "Wed",
    [Thu] = "Thu",
    [Fri] = "Fri",
    [Sat] = "Sat",
    [Sun] = "Sun",
};
person rick-rick-rick    schedule 19.01.2020

Я предпочитаю смешивать массивы и ostream следующим образом:

std::ostream& operator<<(std::ostream& lhs, WeekEnum e) {
    static const std::array<std::string, 7> WEEK_STRINGS = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };

    return os << WEEK_STRINGS[statuc_cast<WeekEnum>(e)]
}

cout << "Today is " << WeekEnum::Monday;

Я также предлагаю использовать класс enum вместо Enum

person Elvis Dukaj    schedule 14.04.2020