Вот что я пытаюсь сделать:
typedef enum { ONE, TWO, THREE } Numbers;
Я пытаюсь написать функцию, которая будет выполнять случай переключения, подобный следующему:
char num_str[10];
int process_numbers_str(Numbers num) {
switch(num) {
case ONE:
case TWO:
case THREE:
{
strcpy(num_str, num); //some way to get the symbolic constant name in here?
} break;
default:
return 0; //no match
return 1;
}
Вместо того, чтобы определять в каждом случае, есть ли способ установить его с помощью переменной enum, как я пытаюсь сделать выше?
c
enums
c-preprocessor
zxcv
источник
источник
Методика создания идентификатора C и строки? можно использовать здесь.
Как обычно с такими препроцессорами, написание и понимание части препроцессора может быть трудным, включая передачу макросов другим макросам и использование операторов # и ##, но использовать это очень просто. Я считаю этот стиль очень полезным для длинных перечислений, где сохранение одного и того же списка дважды может быть действительно проблематичным.
Заводской код - набирается только один раз, обычно скрывается в заголовке:
enumFactory.h:
// expansion macro for enum value definition #define ENUM_VALUE(name,assign) name assign, // expansion macro for enum to string conversion #define ENUM_CASE(name,assign) case name: return #name; // expansion macro for string to enum conversion #define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name; /// declare the access function and define enum values #define DECLARE_ENUM(EnumType,ENUM_DEF) \ enum EnumType { \ ENUM_DEF(ENUM_VALUE) \ }; \ const char *GetString(EnumType dummy); \ EnumType Get##EnumType##Value(const char *string); \ /// define the access function names #define DEFINE_ENUM(EnumType,ENUM_DEF) \ const char *GetString(EnumType value) \ { \ switch(value) \ { \ ENUM_DEF(ENUM_CASE) \ default: return ""; /* handle input error */ \ } \ } \ EnumType Get##EnumType##Value(const char *str) \ { \ ENUM_DEF(ENUM_STRCMP) \ return (EnumType)0; /* handle input error */ \ } \
Завод используется
someEnum.h:
#include "enumFactory.h" #define SOME_ENUM(XX) \ XX(FirstValue,) \ XX(SecondValue,) \ XX(SomeOtherValue,=50) \ XX(OneMoreValue,=100) \ DECLARE_ENUM(SomeEnum,SOME_ENUM)
someEnum.cpp:
#include "someEnum.h" DEFINE_ENUM(SomeEnum,SOME_ENUM)
Эту технику можно легко расширить, чтобы макрос XX принимал больше аргументов, и вы также можете подготовить больше макросов для замены XX для различных нужд, аналогично трем, которые я привел в этом примере.
Сравнение с X-макросами с использованием #include / #define / #undef
Хотя это похоже на X-Macros, о котором упоминали другие, я думаю, что это решение более элегантно, поскольку оно не требует #undefing чего-либо, что позволяет скрыть больше сложных вещей, находящихся на фабрике в файле заголовка - файле заголовка это то, чего вы совсем не касаетесь, когда вам нужно определить новое перечисление, поэтому новое определение перечисления намного короче и чище.
источник
SOME_ENUM(XX)
в точности X-макрос (точнее, «пользовательская форма», которая передаетXX
функцию, а не использует#def
#undef
), а затем, в свою очередь, весь X-MACRO затем передается в DEFINE_ENUM, который его использует. Ничего не отнимать от раствора - работает хорошо. Просто чтобы уточнить, что это использование макросов X.XX
обхода#define
ринга в том, что первый шаблон можно использовать в макрорасширениях. Обратите внимание, что единственные другие решения, столь же краткие, как это, требуют создания и многократного включения отдельного файла для определения нового перечисления.#define DEFINE_ENUM(EnumType) ...
, заменитьENUM_DEF(...)
наEnumType(...)
и попросить пользователя сказать#define SomeEnum(XX) ...
. Препроцессор C будет контекстно расширенSomeEnum
в вызов макроса, если за ним следуют круглые скобки, и в обычный токен в противном случае. (Конечно, это вызывает проблемы, если пользователю нравится использоватьSomeEnum(2)
приведение к типу перечисления, а не(SomeEnum)2
илиstatic_cast<SomeEnum>(2)
.)#define
и#undef
. Вы не согласны с тем, что «форма пользователя» (предлагаемая, например, внизу этой статьи ) является разновидностью x-макроса? Я, конечно, всегда называл это x-макросом, и в кодовых базах C, в которых я был в последнее время, это наиболее распространенная форма (это, очевидно, предвзятое наблюдение). Возможно, я неправильно разбирал OP.// Define your enumeration like this (in say numbers.h); ENUM_BEGIN( Numbers ) ENUM(ONE), ENUM(TWO), ENUM(FOUR) ENUM_END( Numbers ) // The macros are defined in a more fundamental .h file (say defs.h); #define ENUM_BEGIN(typ) enum typ { #define ENUM(nam) nam #define ENUM_END(typ) }; // Now in one and only one .c file, redefine the ENUM macros and reinclude // the numbers.h file to build a string table #undef ENUM_BEGIN #undef ENUM #undef ENUM_END #define ENUM_BEGIN(typ) const char * typ ## _name_table [] = { #define ENUM(nam) #nam #define ENUM_END(typ) }; #undef NUMBERS_H_INCLUDED // whatever you need to do to enable reinclusion #include "numbers.h" // Now you can do exactly what you want to do, with no retyping, and for any // number of enumerated types defined with the ENUM macro family // Your code follows; char num_str[10]; int process_numbers_str(Numbers num) { switch(num) { case ONE: case TWO: case THREE: { strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO" } break; default: return 0; //no match return 1; } // Sweet no ? After being frustrated by this for years, I finally came up // with this solution for my most recent project and plan to reuse the idea // forever
источник
#define ENUM_END(typ) }; extern const char * typ ## _name_table[];
вdefs.h
файле - это объявит вашу таблицу имен в файлах, которые вы используете. (Однако не могу придумать хороший способ объявить размер таблицы.) Кроме того, лично я бы оставил последнюю точку с запятой, но достоинства в любом случае спорны.typ
в очереди#define ENUM_END(typ) };
?Определенно есть способ сделать это - использовать макросы X () . Эти макросы используют препроцессор C для создания перечислений, массивов и блоков кода из списка исходных данных. Вам нужно только добавить новые элементы в #define, содержащий макрос X (). Оператор switch расширится автоматически.
Ваш пример можно записать так:
// Source data -- Enum, String #define X_NUMBERS \ X(ONE, "one") \ X(TWO, "two") \ X(THREE, "three") ... // Use preprocessor to create the Enum typedef enum { #define X(Enum, String) Enum, X_NUMBERS #undef X } Numbers; ... // Use Preprocessor to expand data into switch statement cases switch(num) { #define X(Enum, String) \ case Enum: strcpy(num_str, String); break; X_NUMBERS #undef X default: return 0; break; } return 1;
Есть более эффективные способы (например, использование макросов X для создания массива строк и индекса перечисления), но это простейшая демонстрация.
источник
Я знаю, что у вас есть пара хороших твердых ответов, но знаете ли вы об операторе # в препроцессоре C?
Это позволяет вам делать это:
#define MACROSTR(k) #k typedef enum { kZero, kOne, kTwo, kThree } kConst; static char *kConstStr[] = { MACROSTR(kZero), MACROSTR(kOne), MACROSTR(kTwo), MACROSTR(kThree) }; static void kConstPrinter(kConst k) { printf("%s", kConstStr[k]); }
источник
char const *kConstStr[]
C или C ++ не предоставляют такой возможности, хотя она мне часто нужна.
Следующий код работает, хотя он лучше всего подходит для не разреженных перечислений.
typedef enum { ONE, TWO, THREE } Numbers; char *strNumbers[] = {"one","two","three"}; printf ("Value for TWO is %s\n",strNumbers[TWO]);
Под не разреженным я подразумеваю не в форме
typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;
так как в нем есть огромные пробелы.
Преимущество этого метода в том, что он помещает определения перечислений и строк рядом друг с другом; наличие оператора switch в функции выделяет их. Это означает, что у вас меньше шансов изменить одно без другого.
источник
ПОЦЕЛУЙ. Вы будете делать с вашими перечислениями множество других вещей, связанных с переключателями / регистрами, так почему же печать должна быть другой? Забыть футляр в рутине печати - не такая уж большая проблема, если учесть, что существует около 100 других мест, где вы можете забыть футляр. Просто скомпилируйте -Wall, который предупредит о неполном совпадении регистра. Не используйте "default", потому что это сделает переключение исчерпывающим и вы не получите предупреждений. Вместо этого позвольте переключателю выйти и разобраться со случаем по умолчанию следующим образом ...
const char *myenum_str(myenum e) { switch(e) { case ONE: return "one"; case TWO: return "two"; } return "invalid"; }
источник
Попробуйте преобразовать перечисления C ++ в строки . В комментариях есть улучшения, которые решают проблему, когда элементы перечисления имеют произвольные значения.
источник
Использование препроцессора boost :: делает возможным элегантное решение, подобное следующему:
Шаг 1: включите файл заголовка:
#include "EnumUtilities.h"
Шаг 2: объявите объект перечисления со следующим синтаксисом:
Шаг 3: используйте свои данные:
Получение количества элементов:
td::cout << "Number of Elements: " << TestDataCount << std::endl;
Получение связанной строки:
std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl; std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl; std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;
Получение значения перечисления из связанной строки:
std::cout << "Value of x is " << TestData2Enum("x") << std::endl; std::cout << "Value of y is " << TestData2Enum("y") << std::endl; std::cout << "Value of z is " << TestData2Enum("z") << std::endl;
Это выглядит чистым и компактным, без дополнительных файлов. Код, который я написал в EnumUtilities.h, следующий:
#include <boost/preprocessor/seq/for_each.hpp> #include <string> #define REALLY_MAKE_STRING(x) #x #define MAKE_STRING(x) REALLY_MAKE_STRING(x) #define MACRO1(r, data, elem) elem, #define MACRO1_STRING(r, data, elem) case elem: return REALLY_MAKE_STRING(elem); #define MACRO1_ENUM(r, data, elem) if (REALLY_MAKE_STRING(elem) == eStrEl) return elem; #define MakeEnum(eName, SEQ) \ enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \ last_##eName##_enum}; \ const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \ static std::string eName##2String(const enum eName eel) \ { \ switch (eel) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \ default: return "Unknown enumerator value."; \ }; \ }; \ static enum eName eName##2Enum(const std::string eStrEl) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \ return (enum eName)0; \ };
Есть некоторые ограничения, например, boost :: preprocessor. В этом случае список констант не может быть больше 64 элементов.
Следуя той же логике, вы также можете подумать о создании разреженного перечисления:
#define EnumName(Tuple) BOOST_PP_TUPLE_ELEM(2, 0, Tuple) #define EnumValue(Tuple) BOOST_PP_TUPLE_ELEM(2, 1, Tuple) #define MACRO2(r, data, elem) EnumName(elem) EnumValue(elem), #define MACRO2_STRING(r, data, elem) case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem)); #define MakeEnumEx(eName, SEQ) \ enum eName { \ BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \ last_##eName##_enum }; \ const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \ static std::string eName##2String(const enum eName eel) \ { \ switch (eel) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \ default: return "Unknown enumerator value."; \ }; \ };
В этом случае синтаксис следующий:
MakeEnumEx(TestEnum, ((x,)) ((y,=1000)) ((z,)) );
Использование аналогично приведенному выше (за исключением функции eName ## 2Enum, которую вы могли бы попытаться экстраполировать из предыдущего синтаксиса).
Я тестировал его на Mac и Linux, но имейте в виду, что препроцессор boost :: может быть не полностью переносимым.
источник
Объединив некоторые из приведенных здесь техник, я получил простейшую форму:
#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; }
источник
Если вы используете gcc, можно использовать:
const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};
Тогда просто позвоните, например,
источник
Ознакомьтесь с идеями в Mu Dynamics Research Labs - Blog Archive . Я обнаружил это ранее в этом году - я забыл точный контекст, в котором я его нашел - и адаптировал его в этот код. Мы можем обсудить достоинства добавления E впереди; он применим к конкретной рассматриваемой проблеме, но не является частью общего решения. Я спрятал это в своей папке «виньетки», где я храню интересные фрагменты кода на случай, если они мне понадобятся позже. Стыдно сказать, что в то время я не записал, откуда пришла эта идея.
Заголовок: paste1.h
/* @(#)File: $RCSfile: paste1.h,v $ @(#)Version: $Revision: 1.1 $ @(#)Last changed: $Date: 2008/05/17 21:38:05 $ @(#)Purpose: Automated Token Pasting */ #ifndef JLSS_ID_PASTE_H #define JLSS_ID_PASTE_H /* * Common case when someone just includes this file. In this case, * they just get the various E* tokens as good old enums. */ #if !defined(ETYPE) #define ETYPE(val, desc) E##val, #define ETYPE_ENUM enum { #endif /* ETYPE */ ETYPE(PERM, "Operation not permitted") ETYPE(NOENT, "No such file or directory") ETYPE(SRCH, "No such process") ETYPE(INTR, "Interrupted system call") ETYPE(IO, "I/O error") ETYPE(NXIO, "No such device or address") ETYPE(2BIG, "Arg list too long") /* * Close up the enum block in the common case of someone including * this file. */ #if defined(ETYPE_ENUM) #undef ETYPE_ENUM #undef ETYPE ETYPE_MAX }; #endif /* ETYPE_ENUM */ #endif /* JLSS_ID_PASTE_H */
Пример источника:
/* @(#)File: $RCSfile: paste1.c,v $ @(#)Version: $Revision: 1.2 $ @(#)Last changed: $Date: 2008/06/24 01:03:38 $ @(#)Purpose: Automated Token Pasting */ #include "paste1.h" static const char *sys_errlist_internal[] = { #undef JLSS_ID_PASTE_H #define ETYPE(val, desc) desc, #include "paste1.h" 0 #undef ETYPE }; static const char *xerror(int err) { if (err >= ETYPE_MAX || err <= 0) return "Unknown error"; return sys_errlist_internal[err]; } static const char*errlist_mnemonics[] = { #undef JLSS_ID_PASTE_H #define ETYPE(val, desc) [E ## val] = "E" #val, #include "paste1.h" #undef ETYPE }; #include <stdio.h> int main(void) { int i; for (i = 0; i < ETYPE_MAX; i++) { printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i)); } return(0); }
Не обязательно самое чистое в мире использование препроцессора C, но оно предотвращает многократную запись материала.
источник
Создание чего-то одновременно идентификатора C и строки
источник
Если индекс enum основан на 0, вы можете поместить имена в массив char * и проиндексировать их с помощью значения enum.
источник
#define stringify( name ) # name enum MyEnum { ENUMVAL1 }; ...stuff... stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1
Дальнейшее обсуждение этого метода
Уловки с директивой препроцессора для новичков
источник
Я создал простой шаблонный класс ,
streamable_enum
который использует поток операторов<<
и>>
и основывается наstd::map<Enum, std::string>
:#ifndef STREAMABLE_ENUM_HPP #define STREAMABLE_ENUM_HPP #include <iostream> #include <string> #include <map> template <typename E> class streamable_enum { public: typedef typename std::map<E, std::string> tostr_map_t; typedef typename std::map<std::string, E> fromstr_map_t; streamable_enum() {} streamable_enum(E val) : Val_(val) {} operator E() { return Val_; } bool operator==(const streamable_enum<E>& e) { return this->Val_ == e.Val_; } bool operator==(const E& e) { return this->Val_ == e; } static const tostr_map_t& to_string_map() { static tostr_map_t to_str_(get_enum_strings<E>()); return to_str_; } static const fromstr_map_t& from_string_map() { static fromstr_map_t from_str_(reverse_map(to_string_map())); return from_str_; } private: E Val_; static fromstr_map_t reverse_map(const tostr_map_t& eToS) { fromstr_map_t sToE; for (auto pr : eToS) { sToE.emplace(pr.second, pr.first); } return sToE; } }; template <typename E> streamable_enum<E> stream_enum(E e) { return streamable_enum<E>(e); } template <typename E> typename streamable_enum<E>::tostr_map_t get_enum_strings() { // \todo throw an appropriate exception or display compile error/warning return {}; } template <typename E> std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) { auto& mp = streamable_enum<E>::to_string_map(); auto res = mp.find(e); if (res != mp.end()) { os << res->second; } else { os.setstate(std::ios_base::failbit); } return os; } template <typename E> std::istream& operator>>(std::istream& is, streamable_enum<E>& e) { std::string str; is >> str; if (str.empty()) { is.setstate(std::ios_base::failbit); } auto& mp = streamable_enum<E>::from_string_map(); auto res = mp.find(str); if (res != mp.end()) { e = res->second; } else { is.setstate(std::ios_base::failbit); } return is; } #endif
Применение:
#include "streamable_enum.hpp" using std::cout; using std::cin; using std::endl; enum Animal { CAT, DOG, TIGER, RABBIT }; template <> streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() { return { { CAT, "Cat"}, { DOG, "Dog" }, { TIGER, "Tiger" }, { RABBIT, "Rabbit" } }; } int main(int argc, char* argv []) { cout << "What animal do you want to buy? Our offering:" << endl; for (auto pr : streamable_enum<Animal>::to_string_map()) { // Use from_string_map() and pr.first instead cout << " " << pr.second << endl; // to have them sorted in alphabetical order } streamable_enum<Animal> anim; cin >> anim; if (!cin) { cout << "We don't have such animal here." << endl; } else if (anim == Animal::TIGER) { cout << stream_enum(Animal::TIGER) << " was a joke..." << endl; } else { cout << "Here you are!" << endl; } return 0; }
источник
Вот решение с использованием макросов со следующими функциями:
записывать каждое значение перечисления только один раз, поэтому нет двойных списков для поддержки
не храните значения перечисления в отдельном файле, который позже #included, поэтому я могу записать его где угодно
не заменяйте само перечисление, я все еще хочу, чтобы был определен тип перечисления, но в дополнение к нему я хочу иметь возможность сопоставить каждое имя перечисления с соответствующей строкой (чтобы не влиять на устаревший код)
поиск должен быть быстрым, поэтому желательно без переключателя для этих огромных перечислений
https://stackoverflow.com/a/20134475/1812866
источник
Я думал, что такое решение, как Boost.Fusion one для адаптации структур и классов, было бы неплохим, у них даже было это в какой-то момент, чтобы использовать перечисления в качестве последовательности слияния.
Поэтому я сделал несколько небольших макросов для генерации кода для печати перечислений. Это не идеально и не имеет ничего общего с шаблонным кодом, созданным Boost.Fusion, но может использоваться как макросы Boost Fusion. Я действительно хочу сгенерировать типы, необходимые Boost.Fusion для интеграции в эту инфраструктуру, которая позволяет печатать имена членов структуры, но это произойдет позже, пока это просто макросы:
#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP #define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP #include <swissarmyknife/detail/config.hpp> #include <string> #include <ostream> #include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/stringize.hpp> #include <boost/preprocessor/seq/for_each.hpp> #define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C( \ R, unused, ENUMERATION_ENTRY) \ case ENUMERATION_ENTRY: \ return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY); \ break; /** * \brief Adapts ENUM to reflectable types. * * \param ENUM_TYPE To be adapted * \param ENUMERATION_SEQ Sequence of enum states */ #define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ) \ inline std::string to_string(const ENUM_TYPE& enum_value) { \ switch (enum_value) { \ BOOST_PP_SEQ_FOR_EACH( \ SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C, \ unused, ENUMERATION_SEQ) \ default: \ return BOOST_PP_STRINGIZE(ENUM_TYPE); \ } \ } \ \ inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \ os << to_string(value); \ return os; \ } #endif
Старый ответ ниже довольно плохой, пожалуйста, не используйте его. :)
Старый ответ:
Я искал способ решить эту проблему, не меняя слишком сильно синтаксис объявления перечислений. Я пришел к решению, которое использует препроцессор для извлечения строки из объявления строкового перечисления.
Я могу определять нерезкие перечисления следующим образом:
SMART_ENUM(State, enum State { RUNNING, SLEEPING, FAULT, UNKNOWN })
И я могу взаимодействовать с ними по-разному:
// With a stringstream std::stringstream ss; ss << State::FAULT; std::string myEnumStr = ss.str(); //Directly to stdout std::cout << State::FAULT << std::endl; //to a string std::string myStr = State::to_string(State::FAULT); //from a string State::State myEnumVal = State::from_string(State::FAULT);
На основе следующих определений:
#define SMART_ENUM(enumTypeArg, ...) \ namespace enumTypeArg { \ __VA_ARGS__; \ std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) { \ os << swissarmyknife::enums::to_string(#__VA_ARGS__, val); \ return os; \ } \ \ std::string to_string(const enumTypeArg& val) { \ return swissarmyknife::enums::to_string(#__VA_ARGS__, val); \ } \ \ enumTypeArg from_string(const std::string &str) { \ return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str); \ } \ } \ namespace swissarmyknife { namespace enums { static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) { size_t begin = completeEnumDeclaration.find_first_of('{'); size_t end = completeEnumDeclaration.find_last_of('}'); const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end ); size_t count = 0; size_t found = 0; do { found = identifiers.find_first_of(",}", found+1); if (enumVal == count) { std::string identifiersSubset = identifiers.substr(0, found); size_t beginId = identifiersSubset.find_last_of("{,"); identifiersSubset = identifiersSubset.substr(beginId+1); boost::algorithm::trim(identifiersSubset); return identifiersSubset; } ++count; } while (found != std::string::npos); throw std::runtime_error("The enum declaration provided doesn't contains this state."); } template <typename EnumType> static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) { size_t begin = completeEnumDeclaration.find_first_of('{'); size_t end = completeEnumDeclaration.find_last_of('}'); const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end ); size_t count = 0; size_t found = 0; do { found = identifiers.find_first_of(",}", found+1); std::string identifiersSubset = identifiers.substr(0, found); size_t beginId = identifiersSubset.find_last_of("{,"); identifiersSubset = identifiersSubset.substr(beginId+1); boost::algorithm::trim(identifiersSubset); if (identifiersSubset == enumStr) { return static_cast<EnumType>(count); } ++count; } while (found != std::string::npos); throw std::runtime_error("No valid enum value for the provided string"); } }}
Когда мне понадобится поддержка разреженного перечисления и когда у меня будет больше времени, я улучшу реализации to_string и from_string с помощью boost :: xpressive, но это будет стоить времени компиляции из-за выполнения важных шаблонов и созданного исполняемого файла. вероятно, будет действительно больше. Но у этого есть то преимущество, что он будет более читабельным и удобным, чем этот уродливый код ручной обработки строк.: D
В противном случае я всегда использовал boost :: bimap для выполнения таких сопоставлений между значением перечисления и строкой, но его нужно поддерживать вручную.
источник
Поскольку я предпочитаю не использовать макросы по всем обычным причинам, я использовал более ограниченное решение для макросов, которое имеет то преимущество, что макрос объявления enum остается свободным. Недостатки включают необходимость копировать и вставлять определение макроса для каждого перечисления и необходимость явно добавлять вызов макроса при добавлении значений в перечисление.
std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs) { #define HANDLE(x) case x: os << #x; break; switch (cs) { HANDLE(CaptureState::UNUSED) HANDLE(CaptureState::ACTIVE) HANDLE(CaptureState::CLOSED) } return os; #undef HANDLE }
источник