Как легко отобразить перечислимые переменные С++ в строки
У меня есть набор типов перечислений в некоторых файлах заголовков библиотеки, которые я использую, и я хочу иметь способ преобразования значений перечисления в строки пользователя - и наоборот.
RTTI не сделает этого для меня, потому что "пользовательские строки" должны быть более читабельными, чем перечисления.
Решение с грубой силой будет представлять собой набор функций, подобных этому, но я чувствую, что слишком C-like.
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);
Ответы
Ответ 1
Если вы хотите, чтобы имена перечислений были строками, см. этот пост.
В противном случае, std::map<MyEnum, char const*>
будет работать хорошо. (Нет смысла копировать строковые литералы в std::strings на карте)
Для дополнительного синтаксического сахара, вот как написать класс map_init. Цель состоит в том, чтобы позволить
std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
(eValue1, "A")
(eValue2, "B")
(eValue3, "C")
;
Функция template <typename T> map_init(T&)
возвращает map_init_helper<T>
.
map_init_helper<T>
хранит T & и определяет тривиальное map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&)
. (Возвращение *this
из operator()
позволяет связать operator()
, как operator<<
в std::ostream
s))
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 предлагает те же функциональные возможности из коробки.
Ответ 2
Решение 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;
}
Ответ 3
Автогенерация одной формы из другой.
Источник:
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;
}
Ответ 4
Я помню, что ответил на это в другом месте на 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;
Ответ 5
Я предлагаю использовать сочетание 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)
Ответ 6
Я использую это решение, которое я воспроизвожу ниже:
#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;
}
Ответ 7
Если вы хотите получить строковые представления переменных 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
.
Ответ 8
У меня возникнет соблазн иметь карту m - и вложить ее в перечисление.
с m [MyEnum.VAL1] = "Значение 1";
и все сделано.
Ответ 9
Я несколько раз требовал эту функцию для отладки/анализа кода от других.
Для этого я написал Perl script, который генерирует класс с несколькими перегруженными методами toString
. Каждый метод toString
принимает Enum
в качестве аргумента и возвращает const char*
.
Конечно, script не анализирует С++ для самих перечислений, но использует ctags для генерации таблицы символов.
Perl script находится здесь:
http://heinitz-it.de/download/enum2string/enum2string.pl.html
Ответ 10
Ваши ответы вдохновили меня написать некоторые макросы. Мои требования были следующими:
-
записывать каждое значение перечисления один раз, поэтому нет двойных списков для поддержки
-
не сохраняйте значения enum в отдельном файле, который позже добавлен #, поэтому я могу написать его везде, где захочу
-
не заменяют сам перечисление, я все же хочу иметь тип перечисления, но в дополнение к нему я хочу иметь возможность сопоставлять каждое имя перечисления с соответствующей строкой (чтобы не влиять на устаревший код)
-
поиск должен быть быстрым, поэтому предпочтительно нет переключателя для этих огромных перечислений
Этот код создает классическое перечисление с некоторыми значениями. Кроме того, он создает как 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);
}
Приветствия.
Ответ 11
Вот попытка получить < < и → потоковые операторы на 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;
}
Не уверен в ограничениях этой схемы, хотя... комментарии приветствуются!
Ответ 12
Я потратил больше времени на изучение этой темы, что я хотел бы признать. К счастью, в дикой природе есть отличные решения с открытым исходным кодом.
Это два отличных подхода, даже если они недостаточно известны (пока),
wise_enum
- Автономная интеллектуальная библиотека enum для C++ 14.11.17. Он поддерживает все стандартные функциональные возможности, которые вы ожидаете от класса smart enum в C++.
- Ограничения: требуется минимум C++ 11.
Лучшие перечисления
- Рефлексивная библиотека enum времени компиляции с чистым синтаксисом, в одном заголовочном файле и без зависимостей.
- Ограничения: основаны на макросах, не могут использоваться внутри класса.
Ответ 13
Я просто хотел показать это возможное элегантное решение, используя макросы. Это не решает проблему, но я думаю, что это хороший способ переосмыслить проблему.
#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;
}
---- EDIT ----
После некоторых интернет-исследований и некоторых собственных экспериментов я пришел к следующему решению:
//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;
}
Я просто хотел опубликовать его, возможно, кто-то может найти это решение полезным. Нет необходимости в классах шаблонов, не нуждающихся в С++ 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)};
Ответ 14
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
Ответ 15
в заголовке:
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"
};
Предостережение: не обрабатывать индекс плохих массивов.:) Но вы можете легко добавить функцию для проверки перечисления перед тем, как получить строку из массива.
Ответ 16
Недавно у меня была такая же проблема с библиотекой поставщика (Fincad). К счастью, поставщик предоставил xml doucumentation для всех перечислений. Я закончил создание карты для каждого типа перечисления и предоставления функции поиска для каждого перечисления. Этот метод также позволяет перехватить поиск вне диапазона перечисления.
Я уверен, что 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");
}
}
Похоже, вы хотите пойти другим путем (перечисление на строку, а не строку на перечисление), но это должно быть тривиально, чтобы отменить.
-Whit
Ответ 17
Посмотрите, подходит ли вам следующий синтаксис:
// 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/
Ответ 18
#define stringify( name ) # name
enum MyEnum {
ENUMVAL1
};
...stuff...
stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1
Дальнейшее обсуждение этого метода
Приводы препроцессора для новичков
Ответ 19
этот правильный старый беспорядок - мое усилие, основанное на кусочках и кусочках SO. For_each должен быть расширен для поддержки более 20 значений перечисления. Протестировал его на 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;
}
Как жаль, что вы должны прыгать через обручи с помощью препроцессора, чтобы сделать это на одном из самых популярных языков программирования в мире...
Ответ 20
enum MyEnum {
VAL1, VAL2,VAL3
};
#define StringMyEnum(e) ({__unused MyEnum _e = e;std::string(#e);})
#define CStringMyEnum(e) ({__unused MyEnum _e = e;#e;})
__ unused - это атрибут в GCC/LLVM, тогда вы можете использовать вот так:
std::string s = StringMyEnum(VAL1);
const char *c = CStringMyEnum(VAL1);