Как легко сопоставить перечисления C++ со строками

У меня есть несколько типов перечислений в некоторых файлах заголовков библиотек, которые я использую, и я хочу иметь способ преобразования значений перечисления в пользовательские строки - и наоборот.

RTTI не сделает этого за меня, потому что «пользовательские строки» должны быть немного более читаемыми, чем перечисления.

Решением грубой силы было бы множество подобных функций, но я считаю, что это слишком похоже на C.

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

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

Обновлено: Спасибо за предложения - я должен был прояснить, что перечисления определены в заголовке сторонней библиотеки, поэтому я не хочу менять их определение.

Сейчас я чувствую, что избегаю шаблонов и делаю что-то вроде этого:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);

Возможный дубликат Есть ли простой способ преобразовать перечисление C++ в строку?

Ciro Santilli新疆棉花TRUMP BAN BAD 11.12.2016 18:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
125
1
247 480
20

Ответы 20

Автоматически создавать одну форму из другой.

Источник:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

Создано:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

Если значения перечисления велики, то сгенерированная форма может использовать unordered_map <> или шаблоны, как предлагает Константин.

Источник:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

Создано:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

Пример:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}

Хотя это и быстрее всего, это не так просто, как @MSalters.

kenny 04.12.2008 14:42

Это если у вас есть немного perl / python для чтения списка строк из текстового файла и создания файла .h со статическим символом во время компиляции. = "Написать программы для написания программ"

Martin Beckett 05.03.2009 18:39

@mgb: perl / python - не единственные варианты, которые подойдет практически любой шаблонизатор на любом языке (в этом случае из шаблона генерируются обе формы).

jfs 05.03.2009 19:23

@jf. Да, важным моментом было автоматическое построение статических таблиц данных во время компиляции. Я бы, вероятно, предпочел просто создать тупой статический массив.

Martin Beckett 05.03.2009 19:46

Будет ли это работать, если состояние не известно во время компиляции? Я почти уверен, что это не так - теоретически компилятор должен был бы создать экземпляр шаблона enum2str со всеми возможными значениями перечисления, чего я почти уверен, что gcc (по крайней мере) не будет делать.

Alastair 02.11.2012 19:40

@Alastair: именно поэтому упоминается массив unordered_map<>. Вы создаете то, что подходит для конкретного случая.

jfs 02.11.2012 21:10

Да, но определяющим фактором здесь является не размер перечисления. Я спрашиваю, можно ли написать функцию getStringFromEnum(MyEnum e), используя эту технику, независимо от размера перечисления ...?

Alastair 02.11.2012 23:35

@Alastair: да, ваш собственный ответ (с unordered_map) показывает пример того, как может выглядеть сгенерированный код.

jfs 02.11.2012 23:58

в шапке:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

в файле .cpp:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Предостережение: не обрабатывайте неверный индекс массива. :) Но вы можете легко добавить функцию для проверки перечисления перед получением строки из массива.

Действительно, решение без СУХИХ ТОЧЕК.

xtofl 16.10.2008 15:28

теперь, когда вы упомянули СУХОЙ. файл .h и .cpp, автоматически сгенерированный из другого входного файла. Я бы хотел увидеть лучшие решения (которые не прибегают к ненужной сложности)

moogs 16.10.2008 15:35

Если вы хотите получить строковое представление MyEnumпеременные, шаблоны его не обрежут. Шаблон может быть специализирован на целочисленных значениях, известных во время компиляции.

Однако, если вы этого хотите, попробуйте:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

Это многословно, но будет обнаруживать ошибки, подобные той, которую вы допустили - ваш case VAL1 дублируется.

На самом деле имя метода () не нужно. Смотрите мой ответ.

jfs 16.10.2008 14:38

У меня возникнет соблазн иметь карту m - и встроить ее в перечисление.

настройка с помощью m [MyEnum.VAL1] = "Значение 1";

и все сделано.

Если вы хотите, чтобы сами перечисления назывались строками, см. эта почта. В противном случае std::map<MyEnum, char const*> будет работать нормально. (Нет смысла копировать строковые литералы в std :: strings на карте)

Для дополнительного синтаксического сахара вот как написать класс map_init. Цель состоит в том, чтобы позволить

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

Функция template <typename T> map_init(T&) возвращает map_init_helper<T>. map_init_helper<T> хранит T & и определяет тривиальный map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Возврат *this из operator() позволяет объединить operator() в цепочку, как operator<< на std::ostream)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

Поскольку функция и вспомогательный класс являются шаблонными, вы можете использовать их для любой карты или структуры, подобной карте. Т.е. он также может добавлять записи в std::unordered_map

Если вам не нравится писать эти помощники, boost :: assign предлагает ту же функциональность из коробки.

Вы правы, ссылаясь на другой вопрос. Перед тем, как публиковать сообщения, люди должны просмотреть "похожие вопросы" ...

xtofl 16.10.2008 15:31

@xtofl: «Связанные вопросы», показанные здесь, полностью отличаются от связанных вопросов, перечисленных, когда я разместил вопрос!

Roddy 16.10.2008 16:15

@MSalters, std :: map - полезный способ обработки реализации, но я ищу несколько способов уменьшить шаблонный код, который может потребоваться.

Roddy 16.10.2008 16:17

@MSalters, было бы неплохо иметь возможность принимать несколько аргументов для оператора []. но, к сожалению, этого нельзя сделать. x [a, b] вычисляется как x [b]. в выражении (a, b) используется оператор запятой. поэтому он эквивалентен ["A"] ["B"] ["C"] в вашем коде. вы можете изменить его на [eValue1] ["A"] [eValu ..

Johannes Schaub - litb 05.03.2009 05:05

оператор вызова функции также будет хорошим кандидатом: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... тогда он эквивалентен boost :: assign: insert (MyMap) (eValue1, "A") (eValue2, "B") ... (boost.org/doc/libs/1_35_0/libs/assign/doc/index.html)

Johannes Schaub - litb 05.03.2009 05:13

да - я мог бы заставить это работать, но определяющий оператор (MyEnum, const char *) для этого был бы Evil ++. Согласитесь с boost :: assign.

MSalters 05.03.2009 18:23

+1 за увлекательное объяснение (о map_init) и более простое решение

ϹοδεMεδιϲ 23.05.2012 14:17

Я не думаю, что map_init нужен в новом C++. Можно просто инициализировать карту с помощью std::map<MyEnum, const char*> MyMap = {{eValue1, "A"},{eValue2, "B"},{eValue3, "C"}};

Zitrax 29.06.2017 17:11

@Zitrax: Согласитесь, Аластер отметил это 2 года назад в комментарии к собственному ответу хозяина.

MSalters 29.06.2017 17:21

Недавно у меня была такая же проблема с библиотекой поставщиков (Fincad). К счастью, поставщик предоставил XML-документацию для всех перечислений. В итоге я создал карту для каждого типа перечисления и предоставил функцию поиска для каждого перечисления. Этот метод также позволяет вам перехватывать поиск вне диапазона перечисления.

Я уверен, что swig может сделать что-то подобное для вас, но я рад предоставить утилиты для генерации кода, написанные на ruby.

Вот пример кода:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if (it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

Похоже, вы хотите пойти другим путем (enum to string, а не string to enum), но это должно быть тривиально для обратного.

-Whit

а) Кто-нибудь еще находит это абсолютно нечитаемым? Несколько определений типов и объявлений using улучшат читаемость в значительной степени. б) локальные статические объявления не являются потокобезопасными. c) использовать const string & вместо char *, d) как насчет включения значения, которое не может быть найдено в сгенерированном исключении?

Alastair 04.12.2008 14:28

Я предлагаю сочетание использования X-макросы - лучшее решение и следующих шаблонных функций:

Взять в долг за Marcinkoziukmyopenidcom и продлить

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if ( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

Есть ли способ сделать определение массива строк перечисления универсальным? (Я не знаю, как обрабатывать X-Macro внутри макроса, и мне нелегко справиться с шаблоном)

Jonathan 07.10.2016 18:07

Решение MSalters хорошее, но в основном повторно реализует boost::assign::map_list_of. Если у вас есть ускорение, вы можете использовать его напрямую:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}

Как бы вы использовали это, если eeeToString является членом данных класса? Я получаю сообщение «Ошибка: инициализация элемента данных не разрешена»

User 08.09.2011 21:21

@User: члены данных класса инициализируются в конструкторах, обычно в списке инициализаторов.

MSalters 23.05.2012 16:58

Есть ли способ заставить эту работу работать для всех перечислений. У меня есть несколько объявлений enum, и я не хочу, чтобы карта работала только для типа eee в вашем случае.

jlcv 26.01.2015 19:17

Я попытался использовать шаблон, но потом получил ошибку: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.

jlcv 26.01.2015 19:24

Не очевидно, что вы здесь хотите, но, возможно, псевдоним шаблона C++ 11? например template <typename T> using StringMap = std::unordered_map<T, const char *>.

Alastair 27.01.2015 17:28

На самом деле этот ответ в значительной степени устарел в C++ 11.

Alastair 27.01.2015 17:28

Посмотрите, подходит ли вам следующий синтаксис:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

Если это так, то вы можете прочитать эту статью:
http://www.gamedev.net/reference/snippets/features/cppstringizing/

Мне несколько раз требовалась эта функция для отладки / анализа кода других пользователей. Для этого я написал сценарий Perl, который генерирует класс с несколькими перегруженными методами toString. Каждый метод toString принимает Enum в качестве аргумента и возвращает const char*.

Конечно, сценарий не анализирует C++ на предмет перечислений, но использует теги ctags для создания таблицы символов.

Скрипт Perl находится здесь: http://heinitz-it.de/download/enum2string/enum2string.pl.html

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

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- РЕДАКТИРОВАТЬ ----

После некоторых интернет-исследований и некоторых собственных экспериментов я пришел к следующему решению:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

Я просто хотел опубликовать это, возможно, кто-то найдет это решение полезным. Нет необходимости в классах шаблонов, нет необходимости в C++ 11 и нет необходимости в повышении, поэтому это также можно использовать для простого C.

---- РЕДАКТИРОВАТЬ2 ----

информационная таблица может вызвать некоторые проблемы при использовании более двух перечислений (проблема компилятора). Следующее обходное решение сработало:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

Ваши ответы вдохновили меня написать несколько макросов самостоятельно. Мои требования были следующие:

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

  2. не храните значения перечисления в отдельном файле, который позже #included, поэтому я могу записать его где угодно

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

  4. поиск должен быть быстрым, поэтому желательно без переключателя для этих огромных перечислений

Этот код создает классическое перечисление с некоторыми значениями. Кроме того, он создается как std :: map, который сопоставляет каждое значение перечисления с его именем (например, map [E_SUNDAY] = "E_SUNDAY" и т. д.)

Хорошо, вот код:

EnumUtilsImpl.h:

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // это файл, который вы хотите включить всякий раз, когда вам нужно сделать это, вы будете использовать макросы из него:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // это пример того, как использовать его для создания настраиваемого перечисления:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

Ваше здоровье.

Вот попытка автоматически получить операторы потока << и >> в enum с помощью только однострочной макрос-команды ...

Определения:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

Использование:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

Не уверен в ограничениях этой схемы ... комментарии приветствуются!

Я помню, как отвечал на этот вопрос в другом месте на StackOverflow. Повторение здесь. По сути, это решение, основанное на вариативных макросах, и его довольно легко использовать:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if (isspace(str[i])) continue; \
        else if (str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

Чтобы использовать его в своем коде, просто выполните:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;

Просто измените объявление класса enum на enum, чтобы оно работало до C++ 11.

Debdatta Basu 06.05.2014 15:41

Вы правы, все работает (авто тоже только C++ 11). Хорошее решение! Было бы идеально, если бы вы также могли установить значение для некоторых перечислений

jamk 07.05.2014 11:26

Думаю, я видел в бусте что-то подобное

Serg 09.06.2017 19:33

typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Выше мое простое решение. Одним из преимуществ этого является «ЧИСЛО», которое контролирует размер массива сообщений, а также предотвращает доступ за границу (если вы используете его с умом).

Вы также можете определить функцию для получения строки:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

В дополнение к моему решению я нашел весьма интересным следующее. Это в целом решило проблему синхронизации вышеупомянутой.

Слайды здесь: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Код здесь: https://github.com/arunksaha/enum_to_string

Я использую решение это, которое я воспроизвожу ниже:

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}

Это базовые макросы X, и я поражен тем, что это первый ответ, предлагающий это! +1

Lightness Races in Orbit 10.04.2015 15:45

Я потратил больше времени на изучение этой темы, что хотел бы признать. К счастью, в мире есть отличные решения с открытым исходным кодом.

Это два отличных подхода, пусть и недостаточно хорошо известных (пока).

мудрый_енум

  • Автономная библиотека интеллектуального перечисления для C++ 14/11/17. Он поддерживает все стандартные функции, которые можно ожидать от класса умного перечисления в C++.
  • Ограничения: требуется как минимум C++ 11.

Лучше перечисления

  • Отражательная библиотека перечисления времени компиляции с чистым синтаксисом, в одном файле заголовка и без зависимостей.
  • Ограничения: основан на макросах, не может использоваться внутри класса.

этот правильный старый беспорядок - мои усилия, основанные на битах и ​​деталях от SO. Для поддержки более 20 значений перечисления необходимо расширить for_each. Протестировал на visual studio 2019, clang и gcc. C++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

который производит следующий код

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

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

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

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

Вы предлагаете использовать строки и вообще не использовать перечисления? На самом деле это не решает проблему.

Roddy 09.03.2020 15:21

Используя назначенные инициализаторы массива, ваш строковый массив не зависит от порядка элементов в перечислении:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}

Другие вопросы по теме