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

Предположим, у нас есть несколько именованных перечислений:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Я искал в Google сценарий (на любом языке), который сканирует все заголовки в моем проекте и генерирует заголовок с одной функцией для каждого перечисления.

char* enum_to_string(MyEnum t);

И реализация примерно так:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

Проблема на самом деле связана с перечислениями с определением типа и безымянными перечислениями в стиле C. Кто-нибудь знает что-нибудь по этому поводу?

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

Ответ о фабрике на основе макросов перенесен в stackoverflow.com/questions/147267/… - после того, как вопрос был обновлен, здесь он больше не актуален.

Suma 14.10.2008 19:42
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
126
2
127 010
34
Перейти к ответу Данный вопрос помечен как решенный

Ответы 34

Это практически единственный способ сделать это (также может работать массив строк).

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

Я стараюсь создать массив C с именами в том же порядке и позиции, что и значения перечисления.

например.

enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };

тогда вы можете использовать массив в тех местах, где вам нужно удобочитаемое значение, например

colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];

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

#define printword(XX) cout << #XX;
printword(red);

выведет "красный" на стандартный вывод. К сожалению, это не сработает для переменной (так как вы получите распечатанное имя переменной)

Последнее предостережение (не сработает для переменной) - большой недостаток, но +1 все равно.

chappjc 27.08.2015 20:55

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

kyb 27.08.2017 16:46

Проблема с ответом 0 заключается в том, что двоичные значения перечисления не обязательно начинаются с 0 и не обязательно являются смежными.

Когда мне это нужно, я обычно:

  • вытащить определение перечисления в мой источник
  • отредактируйте его, чтобы получить только имена
  • выполните макрос, чтобы изменить имя на предложение case в вопросе, хотя обычно в одной строке: case foo: return "foo";
  • добавьте переключатель, синтаксис по умолчанию и другой синтаксис, чтобы сделать его законным

X-макросы - лучшее решение. Пример:

#include <iostream>

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
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c];
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

colours.def:

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

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

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.

изящно, хотя мне не нравится лишний файл

Ronny Brendel 14.10.2008 22:02

Просто убедитесь, что ваш процесс сборки не добавляет #pragma (once) перед каждым включаемым файлом ...

xtofl 14.10.2008 22:47

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

Edu Felipe 28.10.2009 23:54

Я не уверен в "лучшем" решении!

Lightness Races in Orbit 04.04.2011 15:18

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

Julien Guertault 12.01.2014 07:40

Одно замечание: в последнем фрагменте кода примера отсутствует хеш. Это должно быть: #define X (a, b) a, #define X (a, b) #b, И первая строка также может быть: #define X (a) a,

ikku100 19.02.2014 13:01

@ ikku100 вы неверны насчет #define X(a, b) #b. Это необходимо только в том случае, если определение выглядит как X(Red, red), а не как определение, показанное в ответе X(Red, "red").

learnvst 21.03.2014 14:26

Потрясающий! @RonnyBrendel, небольшая настройка может сделать его однофайловым решением (см. Мой ответ ниже)

FractalSpace 21.08.2014 01:53

Я думаю, что это также можно адаптировать с помощью #define X(a) case a: return #a; и char *color_str(Colours c){ switch(c){ #define, #include, #undef, default: return "error"; }, и таким образом он будет обрабатывать перечисления с непоследовательными целочисленными значениями.

Philippe Carphin 14.07.2020 02:34

Макрорешение Suma - это хорошо. Однако вам не обязательно иметь два разных макроса. C++ с радостью включит заголовок дважды. Просто оставьте включенную охрану.

Таким образом, у вас будет foobar.h, определяющий только

ENUM(Foo, 1)
ENUM(Bar, 2)

и вы бы включили это так:

#define ENUMFACTORY_ARGUMENT "foobar.h"
#include "enumfactory.h"

enumfactory.h выполнит 2 #include ENUMFACTORY_ARGUMENT. В первом раунде он расширяет ENUM, как DECLARE_ENUM от Suma; во втором раунде ENUM работает как DEFINE_ENUM.

Вы также можете включать enumfactory.h несколько раз, если вы передаете разные # define для ENUMFACTORY_ARGUMENT

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

largest_prime_is_463035818 29.06.2017 18:38
Ответ принят как подходящий

Вы можете проверить GCCXML.

Запуск GCCXML в вашем примере кода дает:

<GCC_XML>
  <Namespace id = "_1" name = "::" members = "_3 " mangled = "_Z2::"/>
  <Namespace id = "_2" name = "std" context = "_1" members = "" mangled = "_Z3std"/>
  <Enumeration id = "_3" name = "MyEnum" context = "_1" location = "f0:1" file = "f0" line = "1">
    <EnumValue name = "FOO" init = "0"/>
    <EnumValue name = "BAR" init = "80"/>
  </Enumeration>
  <File id = "f0" name = "my_enum.h"/>
</GCC_XML>

Вы можете использовать любой язык, который предпочитаете, чтобы извлечь теги Enumeration и EnumValue и сгенерировать желаемый код.

Превосходно! Работал как брелок с простым скриптом на питоне. Спасибо.

Edu Felipe 14.10.2008 23:43

+1, GCCXML выглядит очень красиво! (Хотя я почти упал, поскольку изначально неправильно прочитал это как предложение использовать приведенный выше подробный синтаксис XML для кодирования вашего перечисления - решение, которое пахнет чрезмерной инженерией!)

j_random_hacker 12.03.2009 12:46

какие изменения вы можете опубликовать в скрипте Python?

phillipwei 13.08.2009 20:11

Обратите внимание, что в идеале ваша функция преобразования должна возвращать символ const *.

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

#include "enum_def.h"
#include "colour.h"
#include "enum_conv.h"
#include "colour.h"

Где enum_def.h имеет:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) enum NAME {
#define ENUM_ADD(NAME, VALUE) NAME = VALUE,
#define ENUM_END };

И enum_conv.h имеет:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) {
#define ENUM_ADD(NAME, VALUE) case NAME: return #NAME;
#define ENUM_END default: return "Invalid value"; } }

И, наконец, в colour.h есть:

ENUM_START(colour)
ENUM_ADD(red,   0xff0000)
ENUM_ADD(green, 0x00ff00)
ENUM_ADD(blue,  0x0000ff)
ENUM_END

И вы можете использовать функцию преобразования как:

printf("%s", colour_to_string(colour::red));

Это уродливо, но это единственный способ (на уровне препроцессора), который позволяет вам определять перечисление только в одном месте вашего кода. Таким образом, ваш код не подвержен ошибкам из-за изменений в перечислении. Ваше определение перечисления и функция преобразования всегда будут синхронизированы. Однако, повторяю, это некрасиво :)

Следующий скрипт ruby ​​пытается проанализировать заголовки и построить необходимые источники вместе с исходными заголовками.

#! /usr/bin/env ruby

# Let's "parse" the headers
# Note that using a regular expression is rather fragile
# and may break on some inputs

GLOBS = [
  "toto/*.h",
  "tutu/*.h",
  "tutu/*.hxx"
]

enums = {}
GLOBS.each { |glob|
  Dir[glob].each { |header|
    enums[header] = File.open(header, 'rb') { |f|
      f.read
    }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values|
      [
        enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value|
          enum_key_and_value.split(/\s*=\s*/).first
        }
      ]
    }
  }
}


# Now we build a .h and .cpp alongside the parsed headers
# using the template engine provided with ruby
require 'erb'

template_h = ERB.new <<-EOS
#ifndef <%= enum_name %>_to_string_h_
#define <%= enum_name %>_to_string_h_ 1

#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);

#endif
EOS

template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"

char* enum_to_string(<%= enum_name %> e)
{
  switch (e)
  {<% enum_keys.each do |enum_key| %>
    case <%= enum_key %>: return "<%= enum_key %>";<% end %>
    default: return "INVALID <%= enum_name %> VALUE";
  }
}
EOS

enums.each { |header, enum_name_and_keys|
  enum_name_and_keys.each { |enum_name, enum_keys|
    File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h|
      built_h.write(template_h.result(binding))
    }

    File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
      built_cpp.write(template_cpp.result(binding))
    }
  }
}

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

Допустим, у вас есть заголовок toto / a.h, содержащий определения для перечислений MyEnum и MyEnum2. Скрипт построит:

toto/MyEnum_to_string.h
toto/MyEnum_to_string.cpp
toto/MyEnum2_to_string.h
toto/MyEnum2_to_string.cpp

Более надежными решениями были бы:

  • Соберите все источники, определяющие перечисления и их операции, из другого источника. Это означает, что вы определяете свои перечисления в XML / YML / любом другом файле, который намного проще анализировать, чем C / C++.
  • Используйте настоящий компилятор, например, предложенный Avdi.
  • Используйте макросы препроцессора с шаблонами или без них.

Другой ответ: в некоторых контекстах имеет смысл определить ваше перечисление в некодовом формате, таком как файл CSV, YAML или XML, а затем сгенерировать как код перечисления C++, так и код преобразования из определения. Такой подход может оказаться практичным или непрактичным в вашем приложении, но об этом следует помнить.

QT может вытащить это из (благодаря компилятору метаобъектов):

QNetworkReply::NetworkError error;

error = fetchStuff();

if (error != QNetworkReply::NoError) {

    QString errorValue;

    QMetaObject meta = QNetworkReply::staticMetaObject;

    for (int i=0; i < meta.enumeratorCount(); ++i) {

        QMetaEnum m = meta.enumerator(i);

        if (m.name() == QLatin1String("NetworkError")) {

            errorValue = QLatin1String(m.valueToKey(error));

            break;

        }

    }

    QMessageBox box(QMessageBox::Information, "Failed to fetch",

                "Fetching stuff failed with error '%1`").arg(errorValue),

                QMessageBox::Ok);

    box.exec();

    return 1;

}

In Qt every class that has the Q_OBJECT macro will automatically have a static member "staticMetaObject" of the type QMetaObject. You can then find all sorts of cool things like the properties, signals, slots and indeed enums.

Источник

Я делаю это с помощью отдельных классов-оболочек перечисления бок о бок, которые генерируются с помощью макросов. Есть несколько преимуществ:

  • Может генерировать их для перечислений, которые я не определяю (например: перечисления заголовков платформы ОС)
  • Может включать проверку диапазона в класс-оболочку
  • Может делать более "умное" форматирование с помощью перечислений битовых полей

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

Вот пример перечисления из моей кодовой базы без всего кода фреймворка, который реализует макросы и шаблоны, но вы можете понять:

enum EHelpLocation
{
    HELP_LOCATION_UNKNOWN   = 0, 
    HELP_LOCAL_FILE         = 1, 
    HELP_HTML_ONLINE        = 2, 
};
class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation >
{
public:
    static inline CString FormatEnum( EHelpLocation eValue )
    {
        switch ( eValue )
        {
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE );
        default:
            return FormatAsNumber( eValue );
        }
    }
};
DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > );
typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation;

Идея состоит в том, что вместо использования EHelpLocation вы используете SEHelpLocation; все работает так же, но вы получаете проверку диапазона и метод Format () для самой переменной enum. Если вам нужно отформатировать автономное значение, вы можете использовать CEnumFormatter_EHelpLocation :: FormatEnum (...).

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

@hydroo: Без лишнего файла:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)

#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
    SOME_ENUM(MAKE_ENUM)
};

#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
    SOME_ENUM(MAKE_STRINGS)
};

Мне нравится это решение. Было бы яснее, если бы SOME_UNION и MAKE_UNION назывались SOME_ENUM и MAKE_ENUM.

Bruno Martinez 08.01.2013 18:26

Это отличное решение. У меня самый удобный в обслуживании менеджер ресурсов C++, с которым я когда-либо имел дело.

DCurro 05.07.2014 06:45

Я должен поблагодарить вас за это простое решение :-) - Я немного изменил его, чтобы MetaSyntacticVariableNames[] стал частью объявления класса, создав метод static const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }

DeckerDK 29.01.2015 16:46

Фантастический ответ! Я еще больше упростил его, сгруппировав MAKE_ENUM и MAKE_STRINGS в один макрос, что сделало весь процесс еще проще. Я добавил ответ в этой теме с этим кодом, если кому-то интересно.

Francois Bertrand 10.09.2018 23:26

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

в файле myenummap.h:

#include <map>
#include <string>
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map<unsigned int, std::string>
{
  mymap()
  {
    this->operator[]( one ) = "ONE";
    this->operator[]( two ) = "TWO";
    this->operator[]( three ) = "THREE";
    this->operator[]( five ) = "FIVE";
    this->operator[]( six ) = "SIX";
    this->operator[]( seven ) = "SEVEN";
  };
  ~mymap(){};
};

в main.cpp

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

Это не const, но удобно.

Вот еще один способ, использующий возможности C++ 11. Это константа, не наследует контейнер STL и немного аккуратнее:

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair<int,std::string> mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; 
    std::vector<mapping> const nummap = 
    { 
        m(one,"one"), 
        m(two,"two"), 
        m(three,"three"),
        m(five,"five"),
        m(six,"six"),
        m(seven,"seven"),
    };
    for(auto i  : nummap)
    {
        if (i.first==static_cast<int>(e))
        {
            return i.second;
        }
    }
    return "";
}

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}

Это совершенно законно. Я делаю это все время.

Jonathan Graehl 12.08.2010 03:47

Хорошее решение. Это С ++, поэтому использовать карту stl можно.

Adam Bruss 12.01.2012 21:31

Это неизданное программное обеспечение, но похоже, что BOOST_ENUM от Фрэнка Лауба может соответствовать всем требованиям. Что мне нравится в этом, так это то, что вы можете определить перечисление в рамках класса, чего обычно не позволяет делать большинство перечислений на основе макросов. Он находится в Boost Vault по адресу: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=&. Он не разрабатывался с 2006 года, поэтому я не знаю, насколько хорошо он компилируется с новыми выпусками Boost. Посмотрите в libs / test пример использования.

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

Скачать здесь: http://www.mediafire.com/?nttignoozzz

Тема обсуждения здесь: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Запустите программу с аргументом «--help», чтобы получить описание того, как ее использовать.

Не могли бы вы поместить это где-нибудь в репозиторий (github, google code или bitbucket) и разместить здесь ссылку вместо mediafire? Я бы помог людям, желающим это понять :)

Edu Felipe 09.06.2010 21:50

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

Эта реализация не требует от нет каких-либо изменений в коде, определяющем константы, которые могут быть перечислениями, #define или чем-либо еще, переходящим в целое число - в моем случае у меня были символы, определенные в терминах других символов. Он также хорошо работает с разреженными значениями. Он даже позволяет использовать несколько имен для одного и того же значения, всегда возвращая первое. Единственным недостатком является то, что вам потребуется создать таблицу констант, которая может устареть, например, при добавлении новых.

struct IdAndName
{
   int          id;
   const char * name;
   bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
};
#define ID_AND_NAME(x) { x, #x }

const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
{
   if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
      std::stable_sort(table_begin, table_end);

   IdAndName searchee = { id, NULL };
   IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
   return (p == table_end || p->id != id) ? NULL : p->name;
}

template<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

Пример того, как вы бы это использовали:

static IdAndName WindowsErrorTable[] =
{
   ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
   ID_AND_NAME(NO_ERROR),
   ID_AND_NAME(ERROR_INVALID_FUNCTION),
   ID_AND_NAME(ERROR_FILE_NOT_FOUND),
   ID_AND_NAME(ERROR_PATH_NOT_FOUND),
   ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
   ID_AND_NAME(ERROR_ACCESS_DENIED),
   ID_AND_NAME(ERROR_INVALID_HANDLE),
   ID_AND_NAME(ERROR_ARENA_TRASHED),
   ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
   ID_AND_NAME(ERROR_INVALID_BLOCK),
   ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
   ID_AND_NAME(ERROR_BAD_FORMAT),
   ID_AND_NAME(ERROR_INVALID_ACCESS),
   ID_AND_NAME(ERROR_INVALID_DATA),
   ID_AND_NAME(ERROR_INVALID_DRIVE),
   ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
   ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
   ID_AND_NAME(ERROR_NO_MORE_FILES)
};

const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

Функция IdToName использует std::lower_bound для быстрого поиска, который требует сортировки таблицы. Если первые две записи в таблице не в порядке, функция отсортирует их автоматически.

Обновлено: комментарий заставил меня подумать о другом способе использования того же принципа. Макрос упрощает создание большого оператора switch.

#define ID_AND_NAME(x) case x: return #x

const char * WindowsErrorToName(int id)
{
    switch(id)
    {
        ID_AND_NAME(ERROR_INVALID_FUNCTION);
        ID_AND_NAME(ERROR_FILE_NOT_FOUND);
        ID_AND_NAME(ERROR_PATH_NOT_FOUND);
        ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
        ID_AND_NAME(ERROR_ACCESS_DENIED);
        ID_AND_NAME(ERROR_INVALID_HANDLE);
        ID_AND_NAME(ERROR_ARENA_TRASHED);
        ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
        ID_AND_NAME(ERROR_INVALID_BLOCK);
        ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
        ID_AND_NAME(ERROR_BAD_FORMAT);
        ID_AND_NAME(ERROR_INVALID_ACCESS);
        ID_AND_NAME(ERROR_INVALID_DATA);
        ID_AND_NAME(ERROR_INVALID_DRIVE);
        ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
        ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
        ID_AND_NAME(ERROR_NO_MORE_FILES);
        default: return NULL;
    }
}

Хорошее решение. Но я бы предпочел switch and case, так как он прост и понятен.

Deqing 15.08.2013 08:36

#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

Дальнейшее обсуждение этого метода

Уловки с директивой препроцессора для новичков

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

srcspider 17.07.2016 05:24

Не так давно я проделал хитрость, чтобы перечисления правильно отображались в QComboBox и определяли представления перечислений и строк как одну инструкцию.

#pragma once
#include <boost/unordered_map.hpp>

namespace enumeration
{

   struct enumerator_base : boost::noncopyable
   {
      typedef
         boost::unordered_map<int, std::wstring>
         kv_storage_t;
      typedef
         kv_storage_t::value_type
         kv_type;
      kv_storage_t const & kv() const
      {
         return storage_;
      }

      LPCWSTR name(int i) const
      {
         kv_storage_t::const_iterator it = storage_.find(i);
         if (it != storage_.end())
            return it->second.c_str();
         return L"empty";
      }

   protected:
      kv_storage_t storage_;
   };

   template<class T>
   struct enumerator;

   template<class D>
   struct enum_singleton : enumerator_base
   {
      static enumerator_base const & instance()
      {
         static D inst;
         return inst;
      }
   };
}

#define QENUM_ENTRY(K, V, N)  K, N storage_.insert(std::make_pair((int)K, V));

#define QBEGIN_ENUM(NAME, C)   \
enum NAME                     \
{                             \
   C                          \
}                             \
};                            \
}                             \

#define QEND_ENUM(NAME) \
};                     \
namespace enumeration  \
{                      \
template<>             \
struct enumerator<NAME>\
   : enum_singleton< enumerator<NAME> >\
{                      \
   enumerator()        \
   {

//usage
/*
QBEGIN_ENUM(test_t,
   QENUM_ENTRY(test_entry_1, L"number uno",
   QENUM_ENTRY(test_entry_2, L"number dos",
   QENUM_ENTRY(test_entry_3, L"number tres",
QEND_ENUM(test_t)))))
*/

Теперь у вас есть enumeration::enum_singleton<your_enum>::instance(), способный преобразовывать перечисления в строки. Если вы замените kv_storage_t на boost::bimap, вы также сможете выполнить обратное преобразование. Был введен общий базовый класс для конвертера, чтобы хранить его в объекте Qt, потому что объекты Qt не могли быть шаблонами.

Предыдущее появление

Как вариант, используйте простую lib> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

В коде

#include <EnumString.h>

enum FORM {
    F_NONE = 0,
    F_BOX,
    F_CUBE,
    F_SPHERE,
};

добавить строки

Begin_Enum_String( FORM )
{
    Enum_String( F_NONE );
    Enum_String( F_BOX );
    Enum_String( F_CUBE );
    Enum_String( F_SPHERE );
}
End_Enum_String;

Работает нормально, если значения в enum не дублируются.

Пример использования

enum FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

и наоборот

assert( EnumString< FORM >::To( f, str ) );

#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
private:                                                            \
    static std::map<int, std::string> nameMap;                      \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static std::map<int, std::string> initMap()                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        map<int, string> tmp;                                       \
        for(vector<string>::iterator it = strings.begin();          \
                                               it != strings.end(); \
                                               ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if (buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
        return tmp;                                                 \
    }                                                               \
public:                                                             \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap[aInt];                                       \
    }                                                               \
};                                                                  \
std::map<int, std::string>                                          \
EnumName::nameMap = EnumName::initMap();

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

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
cout<<MyEnum::toString(MyEnum::TWO);
cout<<MyEnum::toString(10);

Мне нравится ваш API, но, к сожалению, ваш SmartEnum на самом деле не создает «тип» перечисления. Вы не можете сделать MyEnum x = MyEnum::TWO;. Я опубликовал свою правку вашего класса, чтобы поддержать это.

Mark Lakata 02.05.2014 00:12

Вот попытка автоматически получить операторы потока << и >> в 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;
}

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

#include <iostream>
#include <map>
#define IDMAP(x) (x,#x)

std::map<int , std::string> enToStr;
class mapEnumtoString
{
public:
    mapEnumtoString(){  }
    mapEnumtoString& operator()(int i,std::string str)
    {
        enToStr[i] = str;
        return *this;
    }
public:
   std::string operator [] (int i)
    {
        return enToStr[i];
    }

};
mapEnumtoString k;
mapEnumtoString& init()
{
    return k;
}

int main()
{

init()
    IDMAP(1)
    IDMAP(2)
    IDMAP(3)
    IDMAP(4)
    IDMAP(5);
std::cout<<enToStr[1];
std::cout<<enToStr[2];
std::cout<<enToStr[3];
std::cout<<enToStr[4];
std::cout<<enToStr[5];
}

Пожалуйста, объясните, почему это ответ.

Alok 25.04.2014 14:51

У меня есть невероятно простой в использовании макрос, который делает это совершенно СУХИМ способом. Он включает в себя вариативные макросы и некоторую простую магию синтаксического анализа. Вот оно:

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

Хорошая идея - использовать строго типизированное перечисление (класс перечисления). Вот демонстрация: cpp.sh/4ife

chappjc 27.08.2015 20:48

Работает ли это с внешне определенными перечислениями / символами. Например, символы, определенные ОС или библиотекой, с пробелами в нумерации?

Jason Harrison 13.10.2015 19:54

Очень красиво, но не компилируется, если поместить внутрь класса (я не мог понять, почему).

AlwaysLearning 02.12.2015 13:28

Мне не удалось скомпилировать это в VS2015. Я получаю предупреждение и ошибку: предупреждение: многострочный комментарий [-Wcomment] #define MAKE_ENUM (name, ...) enum class name {VA_ARGS, __COUNT} error: stray '#' in program std *: string enumName = #имя

Craig.Feied 16.08.2017 05:22

Это модификация ответа @ user3360260. Он имеет следующие новые функции

  • MyEnum fromString(const string&) поддержка
  • компилируется с VisualStudio 2012
  • перечисление - это фактический тип POD (а не только объявления const), поэтому вы можете назначить его переменной.
  • добавлена ​​функция C++ "range" (в виде вектора), чтобы разрешить итерацию "foreach" по enum

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

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
MyEnum foo = MyEnum::TWO;
cout << MyEnum::toString(foo);  // static method
cout << foo.toString();         // member method
cout << MyEnum::toString(MyEnum::TWO);
cout << MyEnum::toString(10);
MyEnum foo = myEnum::fromString("TWO");

// C++11 iteration over all values
for( auto x : MyEnum::allValues() )
{
  cout << x.toString() << endl;
}

Вот код

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
public:                                                             \
    EnumName() : value(0) {}                                        \
    EnumName(int x) : value(x) {}                                   \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static void initMap(std::map<int, std::string>& tmp)                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        for(vector<string>::iterator it = strings.begin();          \
                                                it != strings.end(); \
                                                ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if (buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
    }                                                               \
    int value;                                                      \
public:                                                             \
    operator int () const { return value; }                         \
    std::string toString(void) const {                              \
            return toString(value);                                 \
    }                                                               \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap()[aInt];                                     \
    }                                                               \
    static EnumName fromString(const std::string& s)                \
    {                                                               \
        auto it = find_if (nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { \
            return p.second == s;                                   \
        });                                                         \
        if (it == nameMap().end()) {                                \
        /*value not found*/                                         \
            throw EnumName::Exception();                            \
        } else {                                                    \
            return EnumName(it->first);                             \
        }                                                           \
    }                                                               \
    class Exception : public std::exception {};                     \
    static std::map<int,std::string>& nameMap() {                   \
      static std::map<int,std::string> nameMap0;                    \
      if (nameMap0.size() ==0) initMap(nameMap0);                   \
      return nameMap0;                                              \
    }                                                               \
    static std::vector<EnumName> allValues() {                      \
      std::vector<EnumName> x{ __VA_ARGS__ };                       \
      return x;                                                     \
    }                                                               \
    bool operator<(const EnumName a) const { return (int)*this < (int)a; } \
};         

Обратите внимание, что преобразование toString - это быстрый поиск, а преобразование fromString - это медленный линейный поиск. Но строки в любом случае настолько дороги (и связанный с ними ввод-вывод файла), что я не чувствовал необходимости оптимизировать или использовать bimap.

У вас и user3360260 есть хорошее решение. Почему бы вместо этого не использовать мультикарту?

Vincent 20.01.2015 01:42

Это можно сделать в C++ 11

#include <map>
enum MyEnum { AA, BB, CC, DD };

static std::map< MyEnum, const char * > info = {
   {AA, "This is an apple"},
   {BB, "This is a book"},
   {CC, "This is a coffee"},
   {DD, "This is a door"}
};

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}

Это не отвечает на вопрос OP: он искал способ автоматически генерировать функции, чтобы вернуть имя члена перечисления в виде строки.

Spooky 19.06.2014 03:20

Здесь однофайловое решение (на основе элегантного ответа @Marcin:

#include <iostream>

#define ENUM_TXT \
X(Red) \
X(Green) \
X(Blue) \
X(Cyan) \
X(Yellow) \
X(Magenta) \

enum Colours {
#   define X(a) a,
ENUM_TXT
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
ENUM_TXT
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c] << std::endl;
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

Это было мое решение с BOOST:

#include <boost/preprocessor.hpp>

#define X_STR_ENUM_TOSTRING_CASE(r, data, elem)                                 \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define X_ENUM_STR_TOENUM_IF(r, data, elem)                                     \
    else if (data == BOOST_PP_STRINGIZE(elem)) return elem;

#define STR_ENUM(name, enumerators)                                             \
    enum name {                                                                 \
        BOOST_PP_SEQ_ENUM(enumerators)                                          \
    };                                                                          \
                                                                                \
    inline const QString enumToStr(name v)                                      \
    {                                                                           \
        switch (v)                                                              \
        {                                                                       \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                X_STR_ENUM_TOSTRING_CASE,                                       \
                name,                                                           \
                enumerators                                                     \
            )                                                                   \
                                                                                \
            default:                                                            \
                return "[Unknown " BOOST_PP_STRINGIZE(name) "]";                \
        }                                                                       \
    }                                                                           \
                                                                                \
    template <typename T>                                                       \
    inline const T strToEnum(QString v);                                        \
                                                                                \
    template <>                                                                 \
    inline const name strToEnum(QString v)                                      \
    {                                                                           \
        if (v= = "")                                                               \
            throw std::runtime_error("Empty enum value");                       \
                                                                                \
        BOOST_PP_SEQ_FOR_EACH(                                                  \
            X_ENUM_STR_TOENUM_IF,                                               \
            v,                                                                  \
            enumerators                                                         \
        )                                                                       \
                                                                                \
        else                                                                    \
            throw std::runtime_error(                                           \
                        QString("[Unknown value %1 for enum %2]")               \
                            .arg(v)                                             \
                            .arg(BOOST_PP_STRINGIZE(name))                      \
                                .toStdString().c_str());                        \
    }

Чтобы создать перечисление, объявите:

STR_ENUM
(
    SERVICE_RELOAD,
        (reload_log)
        (reload_settings)
        (reload_qxml_server)
)

Для конверсий:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log");
QString serviceReloadStr = enumToStr(reload_log);

Я хочу опубликовать это на случай, если кто-то сочтет это полезным.

В моем случае мне просто нужно сгенерировать функции ToString() и FromString() для одного перечисления C++ 11 из одного файла .hpp.

Я написал сценарий Python, который анализирует файл заголовка, содержащий элементы перечисления, и генерирует функции в новом файле .cpp.

Вы можете добавить этот сценарий в CMakeLists.txt с помощью execute_process или как событие перед сборкой в ​​Visual Studio. Файл .cpp будет создан автоматически, без необходимости вручную обновлять его каждый раз, когда добавляется новый элемент перечисления.

generate_enum_strings.py

# This script is used to generate strings from C++ enums

import re
import sys
import os

fileName = sys.argv[1]
enumName = os.path.basename(os.path.splitext(fileName)[0])

with open(fileName, 'r') as f:
    content = f.read().replace('\n', '')

searchResult = re.search('enum(.*)\{(.*?)\};', content)
tokens = searchResult.group(2)
tokens = tokens.split(',')
tokens = map(str.strip, tokens)
tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens)

textOut = ''
textOut += '\n#include "' + enumName + '.hpp"\n\n'
textOut += 'namespace myns\n'
textOut += '{\n'
textOut += '    std::string ToString(ErrorCode errorCode)\n'
textOut += '    {\n'
textOut += '        switch (errorCode)\n'
textOut += '        {\n'

for token in tokens:
    textOut += '        case ' + enumName + '::' + token + ':\n'
    textOut += '            return "' + token + '";\n'

textOut += '        default:\n'
textOut += '            return "Last";\n'
textOut += '        }\n'
textOut += '    }\n'
textOut += '\n'
textOut += '    ' + enumName + ' FromString(const std::string &errorCode)\n'
textOut += '    {\n'
textOut += '        if ("' + tokens[0] + '" == errorCode)\n'
textOut += '        {\n'
textOut += '            return ' + enumName + '::' + tokens[0] + ';\n'
textOut += '        }\n'

for token in tokens[1:]:
    textOut += '        else if ("' + token + '" == errorCode)\n'
    textOut += '        {\n'
    textOut += '            return ' + enumName + '::' + token + ';\n'
    textOut += '        }\n'

textOut += '\n'
textOut += '        return ' + enumName + '::Last;\n'
textOut += '    }\n'
textOut += '}\n'

fileOut = open(enumName + '.cpp', 'w')
fileOut.write(textOut)

Пример:

ErrorCode.hpp

#pragma once

#include <string>
#include <cstdint>

namespace myns
{
    enum class ErrorCode : uint32_t
    {
        OK = 0,
        OutOfSpace,
        ConnectionFailure,
        InvalidJson,
        DatabaseFailure,
        HttpError,
        FileSystemError,
        FailedToEncrypt,
        FailedToDecrypt,
        EndOfFile,
        FailedToOpenFileForRead,
        FailedToOpenFileForWrite,
        FailedToLaunchProcess,

        Last
    };

    std::string ToString(ErrorCode errorCode);
    ErrorCode FromString(const std::string &errorCode);
}

Запустите python generate_enum_strings.py ErrorCode.hpp

Результат:

ErrorCode.cpp

#include "ErrorCode.hpp"

namespace myns
{
    std::string ToString(ErrorCode errorCode)
    {
        switch (errorCode)
        {
        case ErrorCode::OK:
            return "OK";
        case ErrorCode::OutOfSpace:
            return "OutOfSpace";
        case ErrorCode::ConnectionFailure:
            return "ConnectionFailure";
        case ErrorCode::InvalidJson:
            return "InvalidJson";
        case ErrorCode::DatabaseFailure:
            return "DatabaseFailure";
        case ErrorCode::HttpError:
            return "HttpError";
        case ErrorCode::FileSystemError:
            return "FileSystemError";
        case ErrorCode::FailedToEncrypt:
            return "FailedToEncrypt";
        case ErrorCode::FailedToDecrypt:
            return "FailedToDecrypt";
        case ErrorCode::EndOfFile:
            return "EndOfFile";
        case ErrorCode::FailedToOpenFileForRead:
            return "FailedToOpenFileForRead";
        case ErrorCode::FailedToOpenFileForWrite:
            return "FailedToOpenFileForWrite";
        case ErrorCode::FailedToLaunchProcess:
            return "FailedToLaunchProcess";
        case ErrorCode::Last:
            return "Last";
        default:
            return "Last";
        }
    }

    ErrorCode FromString(const std::string &errorCode)
    {
        if ("OK" == errorCode)
        {
            return ErrorCode::OK;
        }
        else if ("OutOfSpace" == errorCode)
        {
            return ErrorCode::OutOfSpace;
        }
        else if ("ConnectionFailure" == errorCode)
        {
            return ErrorCode::ConnectionFailure;
        }
        else if ("InvalidJson" == errorCode)
        {
            return ErrorCode::InvalidJson;
        }
        else if ("DatabaseFailure" == errorCode)
        {
            return ErrorCode::DatabaseFailure;
        }
        else if ("HttpError" == errorCode)
        {
            return ErrorCode::HttpError;
        }
        else if ("FileSystemError" == errorCode)
        {
            return ErrorCode::FileSystemError;
        }
        else if ("FailedToEncrypt" == errorCode)
        {
            return ErrorCode::FailedToEncrypt;
        }
        else if ("FailedToDecrypt" == errorCode)
        {
            return ErrorCode::FailedToDecrypt;
        }
        else if ("EndOfFile" == errorCode)
        {
            return ErrorCode::EndOfFile;
        }
        else if ("FailedToOpenFileForRead" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForRead;
        }
        else if ("FailedToOpenFileForWrite" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForWrite;
        }
        else if ("FailedToLaunchProcess" == errorCode)
        {
            return ErrorCode::FailedToLaunchProcess;
        }
        else if ("Last" == errorCode)
        {
            return ErrorCode::Last;
        }

        return ErrorCode::Last;
    }
}

Вот онлайн-генератор: th-thielemann.de/tools/cpp-enum-to-string.html

Th. Thielemann 21.11.2017 10:11

Что ж, еще один вариант. Типичный вариант использования - когда вам нужна константа для HTTP-глаголов, а также используются строковые значения версии.

Пример:

int main () {

  VERB a = VERB::GET;
  VERB b = VERB::GET;
  VERB c = VERB::POST;
  VERB d = VERB::PUT;
  VERB e = VERB::DELETE;


  std::cout << a.toString() << std::endl;

  std::cout << a << std::endl;

  if ( a == VERB::GET ) {
    std::cout << "yes" << std::endl;
  }

  if ( a == b ) {
    std::cout << "yes" << std::endl;
  }

  if ( a != c ) {
    std::cout << "no" << std::endl;
  }

}

Класс VERB:

// -----------------------------------------------------------
// -----------------------------------------------------------
class VERB {

private:

  // private constants
  enum Verb {GET_=0, POST_, PUT_, DELETE_};

  // private string values
  static const std::string theStrings[];

  // private value
  const Verb value;
  const std::string text;

  // private constructor
  VERB (Verb v) :
  value(v), text (theStrings[v])
  {
    // std::cout << " constructor \n";
  }

public:

  operator const char * ()  const { return text.c_str(); }

  operator const std::string ()  const { return text; }

  const std::string toString () const { return text; }

  bool operator == (const VERB & other) const { return (*this).value == other.value; }

  bool operator != (const VERB & other) const { return ! ( (*this) == other); }

  // ---

  static const VERB GET;
  static const VERB POST;
  static const VERB PUT;
  static const VERB DELETE;

};

const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"};

const VERB VERB::GET = VERB ( VERB::Verb::GET_ );
const VERB VERB::POST = VERB ( VERB::Verb::POST_ );
const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ );
const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ );
// end of file

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

Вот хороший пример использования:

enum log_level {INFO, WARNING, ERROR};
...
void logger::write(const std::string log, const log_level l) {
    ...
    std::string s = (l == INFO) ? "INFO" : 
                    (l == WARNING) ? "WARNING" : 
                    (l == ERROR) ? "ERROR" : "UNKNOWN";
    ...
}
...

Конечно, это просто еще один блок операторов switch / if, но это однострочный оператор. А если говорить о лаконичности и простоте, то она встречается где-то посередине. Как постоянное выражение, его также можно легко превратить во встроенную функцию.

Я столкнулся с этим вопросом, когда искал решение своей проблемы с печатью «слов» перечисления на C++. Я вернулся, чтобы предложить простое решение, которое отвечает на поставленный вопрос в том виде, в каком он сформулирован. Все, что требуется, - это «отразить» список перечислений вектором.

enum class genre { Fiction, NonFiction, Periodical, Biography, Children };

vector<string>genre_tbl { "Fiction", "NonFiction", "Periodical", "Biography", "Children" };

Поскольку приведенное выше перечисление по умолчанию выполняет следующие действия;

Fiction = 0
NonFiction = 1
Periodical = 2
Biography = 3
Children = 4

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

string s1 = genre_tbl[int(genre::fiction)];

Для моей проблемы я создал определяемый пользователем класс с именем Book с элементом Gen типа жанра. Программа должна была иметь возможность печатать жанр как слово.

class book {...};
ostream& operator<<(ostream& os, genre g) { return os << genre_tbl[int(g)]; }

book b1;
b1.Gen = genre(0)
cout << b1.Gen;

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

Добавление еще большей простоты использования Фантастический ответ Джаспера Беккерса:

Настроить один раз:

#define MAKE_ENUM(VAR) VAR,
#define MAKE_STRINGS(VAR) #VAR,
#define MAKE_ENUM_AND_STRINGS(source, enumName, enumStringName) \
    enum enumName { \
    source(MAKE_ENUM) \
    };\
const char* const enumStringName[] = { \
    source(MAKE_STRINGS) \
    };

Затем для использования:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)

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

enum class MyEnum
{
    Zero = 0,
    One  = 1,
    Two  = 2
};

ponder::Enum::declare<MyEnum>()
    .value("Zero", MyEnum::Zero)
    .value("One",  MyEnum::One)
    .value("Two",  MyEnum::Two);

ponder::EnumObject zero(MyEnum::Zero);

zero.name(); // -> "Zero"

Этот вопрос дублирует,

Однако ни на один из вопросов я не смог найти хороших ответов.

Вникнув в тему, я нашел два отличных решения с открытым исходным кодом:

мудрый_енум

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

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

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

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

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