Як легко зіставити c ++ перерахунки на рядки


119

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

RTTI не зробить це для мене, тому що "рядки користувачів" повинні бути трохи читабельнішими, ніж перерахування.

Рішення грубої сили було б купою таких функцій, але я відчуваю, що це занадто схоже на С.

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*>буде добре працювати. (Немає сенсу копіювати ваші рядкові літерали в std :: рядки на карті)

Для додаткового синтаксичного цукру, ось як написати клас map_init. Мета - дозволити

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

Функція template <typename T> map_init(T&)повертає a 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 ..
Йоханнес Шауб - ліб

Оператор виклику функцій також був би хорошим кандидатом: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... тоді він еквівалентний boost :: attribute: insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
Йоханнес Шауб - ліб

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

Чи є спосіб зробити цю роботу для всіх перелічених. У мене є кілька декларацій перерахувань, і я не хочу, щоб карта працювала лише для типу eeeу вашому випадку.
Джастін Лян

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

4
Насправді ця відповідь значною мірою застаріла з C ++ 11.
Аластер

19

Автогенеруйте одну форму з іншої.

Джерело:

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

Створено:

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

Якщо значення перерахунків великі, то створена форма може використовувати не упорядковані_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

2
Це якщо у вас є трохи perl / python, щоб прочитати список рядків з текстового файлу та генерувати .h файл зі статичним знаком під час компіляції. = "Пишіть програми для написання програм"
Мартін Бекетт

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

@jf. Так, важливим моментом було побудувати статичні таблиці даних під час компіляції автоматично. Я, мабуть, вважаю за краще просто генерувати німий статичний масив.
Мартін Бекетт

Чи спрацює це, якщо штат не буде відомий під час компіляції? Я впевнений, що цього не вийде - теоретично компілятору доведеться інстанціювати шаблон enum2str усіма можливими значеннями перерахунку, що, напевне, gcc (принаймні) не зробить.
Аластер

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 працював над pre c ++ 11.
Дебдатта Басу

1
Ви маєте рацію, це працює (авто також є лише c ++ 11). Приємне рішення! Було б ідеально, якби ви могли також встановити значення для деяких переліків
джем

Напевно, я бачив у прискоренні щось подібне
Сергій

10

Я пропоную поєднання використання X-макросів - найкраще рішення та наступні функції шаблону:

Позичити марцинкозіукміопенідком і пролонгувати

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)

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

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

Я витратив більше часу на дослідження цієї теми, що хотів би визнати. На щастя, в природі є чудові рішення з відкритим кодом.

Це два чудові підходи, навіть якщо вони недостатньо відомі (поки),

мудрий_enum

  • Автономна бібліотека розумних переліків для C ++ 11/14/17. Він підтримує всі стандартні функціональні можливості, яких можна було б очікувати від розумного перерахунку класу C ++.
  • Обмеження: потрібно щонайменше C ++ 11.

Кращі переліки

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

2

Мені б сподобатися мати карту m - і вбудувати це в перерахунок.

setup з 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. не зберігайте значення enum в окремому файлі, який пізніше #included, тому я можу записати його куди завгодно

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

  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 // це приклад того, як його використовувати для створення користувальницької enum:

#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

Ось спроба запустити операторів << і >> потоку автоматично перерахуванням за допомогою лише однорядкової команди макрокоманд ...

Визначення:

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

Caveat: Не обробляйте поганий індекс масиву. :) Але ви можете легко додати функцію для перевірки перерахунку, перш ніж отримувати рядок з масиву.


Насправді дуже не-DRY-SPOT рішення.
xtofl

тепер, коли ви згадуєте DRY .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.

---- EDIT2 ----

інформаційна таблиця може спричинити деякі проблеми при використанні більш ніж 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!",
};

Вище - моє просте рішення. Однією з переваг цього є "NUM", який контролює розмір масиву повідомлень, він також запобігає позамежному доступу (якщо ви розумно його використовуєте).

Ви також можете визначити функцію отримання рядка:

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 для всіх перелічених. Я в кінцевому підсумку створив карту для кожного типу enum та надав функцію пошуку для кожного enum. Ця методика також дозволяє перехопити пошук за межами діапазону перерахунку.

Я впевнений, що swig може зробити щось подібне для вас, але я радий надати утиліти для генерації коду, написані в рубіні.

Ось зразок коду:

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

Здається, ви хочете піти іншим шляхом (перерахунок на рядок, а не рядок на перерахунок), але це має бути тривіально для зворотного перегляду.

-Чакай


1
а) Хтось інший вважає це абсолютно нечитабельним? Кілька типів набору та використання декларацій значно покращить читабельність. б) локальні статичні декларації не є безпечними для потоків. в) використовувати 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. For_each доведеться розширити, щоб підтримувати більше 20 значень enum. Тестували його на візуальній студії 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:

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.