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


119

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

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

Ответы:


60

Если вы хотите, чтобы перечисления назывались строками, см. Этот пост . В противном случае std::map<MyEnum, char const*>подойдет a . (Нет смысла копировать строковые литералы в 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::ostreams)

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

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

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

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

оператор вызова функции также будет хорошим кандидатом: 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 )
Йоханнес Шауб - litb

31

Решение 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: члены данных класса инициализируются в конструкторах, обычно в списке инициализаторов.
MSalters

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

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

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

19

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

Источник:

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.
Кенни

2
Это если у вас есть немного perl / python для чтения списка строк из текстового файла и создания файла .h со статическим символом во время компиляции. = «Пишите программы для написания программ»
Мартин Беккет,

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

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

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

11

Я помню, как ответил на этот вопрос в другом месте на 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;

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

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

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

10

Я предлагаю сочетание использования 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 внутри макроса, и я не могу легко справиться с шаблоном)
Джонатан

5

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

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

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

4

Если вы хотите получить строковое представление 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

3

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

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

wise_enum

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

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

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

2

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

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

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


2

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

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

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


2

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

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

Приветствия.


2

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

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


1

в шапке:

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

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

1

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

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


1

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

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

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

0

Недавно у меня была такая же проблема с библиотекой поставщиков (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


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

0

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

// 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/


0

этот правильный старый беспорядок - мои усилия, основанные на фрагментах из 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; 
}

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


0

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

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.