Как легко отобразить перечисления C++ в строки
У меня есть несколько типов enum в некоторых заголовочных файлах библиотеки, которые я использую, и я хочу иметь способ преобразования значений enum в пользовательские строки - и наоборот.
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);
25 ответов
Если вы хотите, чтобы перечисление называло себя как строки, посмотрите этот пост. В противном случае, 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 предлагает те же функциональные возможности из коробки.
Решение 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;
}
Автоматически генерировать одну форму из другой.
Источник:
enum {
VALUE1, /* value 1 */
VALUE2, /* value 2 */
};
Сформирован:
const char* enum2str[] = {
"value 1", /* VALUE1 */
"value 2", /* VALUE2 */
};
Если значения enum велики, то сгенерированная форма может использовать 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;
}
Я предлагаю сочетание использования X-макросов - лучшее решение и следующие функции шаблона:
Marcin Koziuk и продлен
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)
Я помню, что ответил на это в другом месте на Stackru. Повторяя это здесь. По сути, это решение, основанное на вариационных макросах, и довольно простое в использовании:
#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;
Я знаю, что опаздываю на вечеринку, но для всех, кто заходит на эту страницу, вы могли бы попробовать это, это проще, чем все там, и имеет больше смысла:
namespace texs {
typedef std::string Type;
Type apple = "apple";
Type wood = "wood";
}
Я потратил больше времени на изучение этой темы, что я хотел бы признать. К счастью, в дикой природе есть отличные решения с открытым исходным кодом.
Это два отличных подхода, даже если они недостаточно известны (пока),
- Автономная библиотека smart enum для C++11/14/17. Он поддерживает все стандартные функциональные возможности, которые вы ожидаете от класса smart enum в C++.
- Ограничения: требуется как минимум C++ 11.
- Рефлексивная библиотека enum времени компиляции с чистым синтаксисом, в одном заголовочном файле и без зависимостей.
- Ограничения: основаны на макросах, не могут использоваться внутри класса.
Я использую это решение, которое я воспроизвожу ниже:
#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;
}
Если вы хотите получить строковые представления 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
дублируется.
Ваши ответы вдохновили меня написать несколько макросов самостоятельно. Мои требования были следующие:
записывайте каждое значение перечисления только один раз, чтобы не было двойных списков
не храните значения enum в отдельном файле, который позже #include, поэтому я могу записать его где угодно
не заменяйте само перечисление, я все еще хочу определить тип перечисления, но в дополнение к этому я хочу иметь возможность сопоставлять каждое имя перечисления с соответствующей строкой (чтобы не влиять на устаревший код)
поиск должен быть быстрым, поэтому желательно, чтобы без этих переключателей
Этот код создает классическое перечисление с некоторыми значениями. Кроме того, он создает как 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);
}
Приветствия.
Я потребовал эту функцию несколько раз для отладки / анализа кода от других. Для этого я написал Perl-скрипт, который генерирует класс с несколькими перегруженными toString
методы. каждый toString
метод принимает Enum
в качестве аргумента и возвращает const char*
,
Конечно, скрипт сам не анализирует C++ для перечислений, но использует ctags для генерации таблицы символов.
Сценарий Perl находится здесь: http://heinitz-it.de/download/enum2string/enum2string.pl.html
Используя назначенные инициализаторы массива, ваш строковый массив не зависит от порядка элементов в перечислении:
enum Values {
Val1,
Val2
};
constexpr string_view v_name[] = {
[Val1] = "Value 1",
[Val2] = "Value 2"
}
Я был бы соблазн иметь карту m - и встраивать ее в перечисление.
настройка с помощью m[MyEnum.VAL1] = "Значение 1";
и все сделано.
Вот попытка автоматически получить операторы << и >> в 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;
}
Не уверен насчет ограничений этой схемы, хотя... комментарии приветствуются!
А как насчет довольно переносимой реализации класса Enum?
Он не очень оптимизирован для облегчения понимания.
#define FOREACH_FRUIT(item) \
item(apple) \
item(orange) \
item(grape, 5) \
item(banana) \
Нет необходимости повторять или обновлять копию определения.
class EnumClass
{
#define GENERATE_ENUM(ENUM, ...) ENUM,
#define GENERATE_STRINGS(STRING, ...) { #STRING, ##__VA_ARGS__ },
#define GENERATE_SIZE(...) + 1
public:
enum Enum {
FOREACH_FRUIT(GENERATE_ENUM) // apple, orange, grape, banana,
} _;
EnumClass(Enum init)
{
_ = init; // grape(2)
_EnumItem build[itemsNo] = { FOREACH_FRUIT(GENERATE_STRINGS) }; // _EnumItem build[itemsNo] = { { "apple" }, { "orange" }, { "grape",5 }, { "banana" }, };
int pos = 0;
for (int i = 0; i < itemsNo; i++)
{
items[i].Name = build[i].Name;
if (0 == build[i].No) {
items[i].No = pos;
for (int j = i; j--;)
{
if (items[j].No == pos)
throw "Existing item # !";
}
pos++;
}
else {
int destPos = build[i].No;
if (destPos < pos) {
for (int j = 0; j < i; j++)
{
if (items[j].No == destPos)
throw "Existing item # !";
}
}
items[i].No = destPos;
pos = destPos + 1;
}
}
}
operator int()
{
return items[_].No;
}
operator char*()
{
return items[_].Name;
}
EnumClass& operator ++(int)
{
if (_ == itemsNo - 1) {
throw "Out of Enum options !";
}
_ = static_cast<EnumClass::Enum>(_ + 1);
return *this;
}
EnumClass& operator --(int)
{
if (0 == _) {
throw "Out of Enum options !";
}
_ = static_cast<EnumClass::Enum>(_ - 1);
return *this;
}
EnumClass operator =(int right)
{
for (int i = 0; i < itemsNo; i++)
{
if (items[i].No == right)
{
_ = static_cast<EnumClass::Enum>(i);
return *this;
}
}
throw "Enum option does not exist !";
}
EnumClass operator =(char *right)
{
for (int i = 0; i < itemsNo; i++)
{
if (!strcmp(items[i].Name, right))
{
_ = static_cast<EnumClass::Enum>(i);
return *this;
}
}
throw "Enum option does not exist !";
}
protected:
static const int itemsNo = FOREACH_FRUIT(GENERATE_SIZE); // + 1 + 1 + 1 + 1;
struct _EnumItem {
char *Name;
int No;
} items[itemsNo]; // { Name = "apple" No = 0 }, { Name = "orange" No = 1 } ,{ Name = "grape" No = 5 } ,{ Name = "banana" No = 6 }
#undef GENERATE_ENUM
#undef GENERATE_STRINGS
#undef GENERATE_SIZE
};
Теперь вы можете выполнять любые стандартные операции + проверять определения и операции выполнения:
int main()
{
EnumClass ec(EnumClass::grape);
ec = "banana"; // ec {_=banana (3)...}
ec--; // ec {_=grape (2)...}
char *name = ec;
int val = ec; // 5
printf("%s(%i)", name, val); // grape(5)
return 0;
}
printf проблема ... " Компилятор технически не знает, какой тип требуется. "
#include <vector>
#include <string>
//Split one comma-separated value string to vector
std::vector<std::string> split(std::string __csv, char __separator){/*trivial*/}
//Initializer
#define ENUMIFY(name, ...) \
struct name \
{ \
enum class Enum \
{ \
__VA_ARGS__ \
}; \
static const std::vector<std::string>& Names() \
{ \
const static std::vector<std::string> _{split(#__VA_ARGS__, ',')}; \
return _; \
}; \
};
Декларация:
ENUMIFY(States, INIT, ON, OFF, RUNNING)
Затем доступны все перечисления, плюс их строки векторизованы:
std::string enum_str = States::Names()[(int)States::ON];
В шапке:
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"
};
Предостережение: не обрабатывайте неверный индекс массива.:) Но вы можете легко добавить функцию для проверки перечисления перед получением строки из массива.
Я просто хотел показать это возможное элегантное решение с использованием макросов. Это не решает проблему, но я думаю, что это хороший способ переосмыслить проблему.
#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 и нет необходимости в boost, так что это также можно использовать для простого 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)};
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
Это мое решение, я ссылаюсь на некоторые другие конструкции, но мой более полный и простой в использовании.
// file: enum_with_string.h
#pragma once
#include <map>
#include <string>
#include <vector>
namespace EnumString {
template <typename T>
static inline void split_string_for_each(const std::string &str,
const std::string &delimiter,
const T &foreach_function,
ssize_t max_number = -1) {
ssize_t num = 0;
std::string::size_type start;
std::string::size_type end = -1;
while (true) {
start = str.find_first_not_of(delimiter, end + 1);
if (start == std::string::npos) break; // over
end = str.find_first_of(delimiter, start + 1);
if (end == std::string::npos) {
foreach_function(num, str.substr(start));
break;
}
foreach_function(num, str.substr(start, end - start));
++num;
if (max_number > 0 && num == max_number) break;
}
}
/**
* Strip function, delete the specified characters on both sides of the string.
*/
inline std::string &strip(std::string &s,
const std::string &characters = " \t\r\n") {
s.erase(0, s.find_first_not_of(characters));
return s.erase(s.find_last_not_of(characters) + 1);
}
static inline std::map<int, std::string> ParserEnumDefine(
const std::string &define_str) {
int cur_num = 0;
std::string cur_item_str;
std::map<int, std::string> result_map;
split_string_for_each(define_str, ",", [&](int num, const std::string &str) {
split_string_for_each(
str, "=",
[&](int num, const std::string &str) {
if (num == 0) cur_item_str = str;
if (num == 1) cur_num = std::stoi(str);
},
2);
result_map.emplace(cur_num, strip(cur_item_str));
cur_num++;
});
return result_map;
}
} // namespace EnumString
/**
* Example:
* @code
* @endcode
*/
#define ENUM_WITH_STRING(Name, ...) \
enum class Name { __VA_ARGS__, __COUNT }; \
static inline const std::string &to_string(Name value) { \
static const auto map = EnumString::ParserEnumDefine(#__VA_ARGS__); \
static const std::string cannot_converted = \
"Cannot be converted to string"; \
int int_value = (int)value; \
if (map.count(int_value)) \
return map.at(int_value); \
else \
return cannot_converted; \
}
Вы можете использовать это так:
#include <iostream>
#include "enum_with_string.h"
ENUM_WITH_STRING(Animal, dog, cat, monkey = 50, fish, human = 100, duck)
int main() {
std::cout << to_string(Animal::dog) << std::endl;
std::cout << to_string(Animal::cat) << std::endl;
std::cout << to_string(Animal::monkey) << std::endl;
std::cout << to_string(Animal::fish) << std::endl;
std::cout << to_string(Animal::human) << std::endl;
std::cout << to_string(Animal::duck) << std::endl;
}
У меня есть github gist.
Недавно у меня была такая же проблема с библиотекой вендора (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");
}
}
Похоже, вы хотите пойти другим путем (перечисление в строку, а не перечисление в строку), но это должно быть тривиальным, чтобы изменить.
-Whit
Посмотрите, подходит ли вам следующий синтаксис:
// 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/
Этот правильный старый беспорядок - мои усилия, основанные на фрагментах из 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;
}
Жаль, что вам придется прыгнуть с препроцессором, чтобы сделать это на одном из наиболее часто используемых языков программирования в мире...
Неэффективно, но работу выполнил. Он может обрабатывать неограниченное количество значений перечисления.
Например
DEFINE_ENUM(Fruit, int32_t, PEAR = -100, APPLE, BANANA, ORANGE = 100, MANGO, STRAWBERRY = 75, WATERMELON = 100)
Он генерирует
enum class Fruit : int32_t {
PEAR = -100, APPLE, BANANA, ORANGE = 100, MANGO, STRAWBERRY = 75, WATERMELON = 100
};
[[nodiscard]] static inline std::string EnumFruitToString(const Fruit& value, bool includeEnumName = false) {
using underlying = typename std::underlying_type<Fruit>::type;
std::string values = "PEAR = -100, APPLE, BANANA, ORANGE = 100, MANGO, STRAWBERRY = 75, WATERMELON = 100";
underlying integralValue = 0;
std::vector<std::string> valuePairs = StringExtensions::Split(values, ", ");
std::map<underlying, std::string> dictionary{};
for (std::string str : valuePairs) {
std::vector<std::string> keyAndValue = StringExtensions::Split(StringExtensions::Remove((includeEnumName ? "Fruit"s + "::"s : ""s) + str, " "), "=");
if (keyAndValue.size() == 1) {
dictionary.emplace(integralValue, keyAndValue[0]);
integralValue += 1;
} else {
dictionary.emplace(StringExtensions::ToNumber<underlying>(keyAndValue[1]), keyAndValue[0]);
integralValue = StringExtensions::ToNumber<underlying>(keyAndValue[1]) + 1;
}
}
return StringExtensions::Trim(dictionary.at(static_cast<underlying>(value)));
}
Другой пример.
DEFINE_DEFAULT_ENUM(Animal, Dog, Cat, Monkey = 50, Fish, Human = 100, Duck, __COUNT)
Он генерирует
enum class Animal : int {
Dog,
Cat,
Monkey = 50,
Fish,
Human = 100,
Duck,
__COUNT
};
[[nodiscard]] static inline std::string EnumAnimalToString(const Animal& value, bool includeEnumName = false) {
using underlying = typename std::underlying_type<Animal>::type;
std::string values = "Dog, Cat, Monkey = 50, Fish, Human = 100, Duck, __COUNT";
underlying integralValue = 0;
std::vector<std::string> valuePairs = StringExtensions::Split(values, ", ");
std::map<underlying, std::string> dictionary{};
for (std::string str : valuePairs) {
std::vector<std::string> keyAndValue = StringExtensions::Split(StringExtensions::Remove((includeEnumName ? "Animal"s + "::"s : ""s) + str, " "), "=");
if (keyAndValue.size() == 1) {
dictionary.emplace(integralValue, keyAndValue[0]);
integralValue += 1;
} else {
dictionary.emplace(StringExtensions::ToNumber<underlying>(keyAndValue[1]), keyAndValue[0]);
integralValue = StringExtensions::ToNumber<underlying>(keyAndValue[1]) + 1;
}
}
return StringExtensions::Trim(dictionary.at(static_cast<underlying>(value)));
}
Код C++.
Вы можете протестировать/запустить его на https://godbolt.org/z/PzMqfGoae .
#include <iostream>
#include <string>
#include <iostream>
#include <type_traits>
#include <vector>
#include <map>
#include <sstream>
#define SPACE_STRING (" "s)
#define EQUAL_SIGN_STRING ("="s)
#define ARGUMENT_SEPARATOR_STRING (", "s)
#define NAMESPACE_SEPARATOR_STRING ("::"s)
#define EMPTY_STRING (""s)
using namespace std::string_literals;
#define TOSTR(value) std::to_string(value)
class StringExtensions
{
public:
static std::string Remove(const std::string& data, const std::string& toRemove)
{
std::string result = data;
size_t pos = 0;
while ((pos = result.find(toRemove, pos)) != std::string::npos)
{
result.erase(pos, toRemove.length());
}
return result;
}
[[nodiscard]] static std::vector<std::string> Split(const std::string& data, const std::string& toFind)
{
std::vector<std::string> v;
if (data.empty() || toFind.empty())
{
v.push_back(data);
return v;
}
size_t ini = 0;
size_t pos;
while ((pos = data.find(toFind, ini)) != std::string::npos)
{
std::string s = data.substr(ini, pos - ini);
if (!s.empty())
{
v.push_back(s);
}
ini = pos + toFind.length();
}
if (ini < data.length())
{
v.push_back(data.substr(ini));
}
return v;
}
[[nodiscard]] static std::string TrimStart(const std::string& data)
{
std::string s = data;
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch)
{ return !std::isspace(ch); }));
return s;
}
[[nodiscard]] static std::string TrimEnd(const std::string& data)
{
std::string s = data;
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch)
{ return !std::isspace(ch); })
.base(),
s.end());
return s;
}
[[nodiscard]] static std::string Trim(const std::string& data)
{
return TrimEnd(TrimStart(data));
}
template <typename T>
[[nodiscard]] static T ToNumber(const std::string& str) requires std::is_integral_v<T> {
std::stringstream ss(str);
T result;
ss >> result;
return result;
}
private:
};
class EnumExtensions {
public:
template <typename T>
[[nodiscard]] static std::string ToEnumValueString(const T& enumValue)
requires std::is_enum_v<T>
{
using UnderlyingType = typename std::underlying_type<T>::type;
UnderlyingType integralValue = static_cast<UnderlyingType>(enumValue);
return TOSTR(integralValue);
}
};
#define DEFINE_ENUM(Name,UnderlyingT,...) \
enum class Name : UnderlyingT { __VA_ARGS__ }; \
[[nodiscard]] static inline std::string Enum##Name##ToString(const Name &value, bool includeEnumName=false) { \
using underlying = typename std::underlying_type<Name>::type; \
std::string values = #__VA_ARGS__; \
underlying integralValue = 0; \
std::vector<std::string> valuePairs = StringExtensions::Split(values, ARGUMENT_SEPARATOR_STRING); \
std::map<underlying, std::string> dictionary{}; \
for (std::string str : valuePairs) \
{ \
std::vector<std::string> keyAndValue = StringExtensions::Split(StringExtensions::Remove((includeEnumName? #Name##s + NAMESPACE_SEPARATOR_STRING : EMPTY_STRING) + str, SPACE_STRING), EQUAL_SIGN_STRING); \
if (keyAndValue.size() == 1) \
{ \
dictionary.emplace(integralValue, keyAndValue[0]); \
integralValue += 1; \
} \
else \
{ \
dictionary.emplace(StringExtensions::ToNumber<underlying>(keyAndValue[1]), keyAndValue[0]); \
integralValue = StringExtensions::ToNumber<underlying>(keyAndValue[1]) + 1; \
} \
} \
return StringExtensions::Trim(dictionary.at(static_cast<underlying>(value))); \
} \
#define DEFINE_DEFAULT_ENUM(Name,...) DEFINE_ENUM(Name,int,__VA_ARGS__)
namespace MyNamespace
{
DEFINE_ENUM(Fruit, int32_t, PEAR = -100, APPLE, BANANA, ORANGE = 100, MANGO, STRAWBERRY = 75, WATERMELON = 100)
DEFINE_DEFAULT_ENUM(Animal, Dog, Cat, Monkey = 50, Fish, Human = 100, Duck, __COUNT)
}
#define PRINT_ENUM(enumName,value,underlyingT,includeEnumName) std::cout << Enum##enumName##ToString(value, includeEnumName) << " --- " << EnumExtensions::ToEnumValueString(value) << std::endl
int main() {
using namespace MyNamespace;
//std::cout << EnumFruitToString(Fruit::PEAR, true) << " --- " << EnumExtensions::ToEnumValueString(Fruit::PEAR) << std::endl
PRINT_ENUM(Fruit,Fruit::PEAR, int32_t, true);
PRINT_ENUM(Fruit, Fruit::APPLE, int32_t, true);
PRINT_ENUM(Fruit, Fruit::BANANA, int32_t, true);
PRINT_ENUM(Fruit, Fruit::ORANGE, int32_t, true);
PRINT_ENUM(Fruit, Fruit::MANGO, int32_t, true);
PRINT_ENUM(Fruit, Fruit::STRAWBERRY, int32_t, true);
PRINT_ENUM(Fruit, Fruit::WATERMELON, int32_t, true);
//std::cout << EnumAnimalToString(Animal::Dog, true) << " --- " << EnumExtensions::ToEnumValueString(Animal::Dog) << std::endl
PRINT_ENUM(Animal, Animal::Dog, int, true);
PRINT_ENUM(Animal, Animal::Cat, int, true);
PRINT_ENUM(Animal, Animal::Monkey, int, false);
PRINT_ENUM(Animal, Animal::Fish, int, false);
PRINT_ENUM(Animal, Animal::Human, int, false);
PRINT_ENUM(Animal, Animal::Duck, int, false);
PRINT_ENUM(Animal, Animal::__COUNT, int, false);
std::cin.get();
return 0;
}
Выход
Fruit::PEAR --- -100
Fruit::APPLE --- -99
Fruit::BANANA --- -98
Fruit::ORANGE --- 100
Fruit::MANGO --- 101
Fruit::STRAWBERRY --- 75
Fruit::ORANGE --- 100
Animal::Dog --- 0
Animal::Cat --- 1
Monkey --- 50
Fish --- 51
Human --- 100
Duck --- 101
__COUNT --- 102
VisualС++
ClangX86-64 - Godbolt.org
Новое обновление. Альтернатива. Этот синтаксис немного уродлив. Но генерирует более эффективный код.
Более полный/эффективный код. Но может обрабатывать до 170 значений перечисления. Если вам нужно более 170, не стесняйтесь изменять этот код. Этот код генерирует методы расширения для управления перечислением.
- ToString — возвращает имя перечисления. Если другое перечисление имеет то же числовое значение, возвращаемое имя является первым перечислением со значением.
- ToIntegralString — возвращает числовое значение в строку.
- ToIntegral — возвращает числовое значение перечисления. Тип является базовым типом перечисления.
- Разбор — преобразовать строку в значение перечисления, может вызвать исключение.
- Анализ — преобразование числового значения в значение перечисления, тип числового значения — это тот же неверный тип перечисления, он может генерировать исключение.
- GetValues — возвращает вектор со всеми значениями enun.
Синтаксис EZNUM_ENUM(EnumName,Var1,Oper1,Val1,Var2,Oper2,Val2,......,Var170,Oper170,Val170)
EZNUM_ENUM_UT(EnumName,UType,Var1,Oper1,Val1,Var2,Oper2,Val2,......,Var170,Oper170,Val170)
- EnumName — имя класса перечисления.
- UType — имя базового типа.
- VarN — имя значения перечисления.
- OperN — оператором присваивания может быть EQ для равенства или _ для отсутствия оператора.
- ValN — значение базового типа. Если ОперН
_
это значение игнорируется.
этому макросу нужны группы по 3 – примеры Var,Oper,Val:
X,_,_
он генерирует X
Y,EQ,2
| он генерирует Y = 2
Z,_,2
| он генерирует Z
Например
EZNUM_ENUM(MobaGame,
Dota2, EQ, 100,
LeagueOfLegends, EQ, 101,
HeroesOfTheStorm, EQ, 102,
Smite, EQ, 103,
Vainglory, EQ, 104,
ArenaOfValor, EQ, 105,
Paragon, EQ, 106,
HeroesOfNewerth, EQ, -100)
Он генерирует
enum class MobaGame : int
{
Dota2 = 100,
LeagueOfLegends = 101,
HeroesOfTheStorm = 102,
Smite = 103,
Vainglory = 104,
ArenaOfValor = 105,
Paragon = 106,
HeroesOfNewerth = -100,
};
class MobaGameEnumExtensions
{
public:
[[nodiscard]] static String ToIntegralString(const MobaGame &value)
{
using namespace Extensions;
return EnumExtensions::ToIntegralString(value);
}
[[nodiscard]] static int ToIntegral(const MobaGame &value)
{
using namespace Extensions;
return EnumExtensions::ToIntegral<MobaGame>(value);
}
[[nodiscard]] static std::string ToString(const MobaGame &value, bool includeEnumName = false)
{
using namespace Extensions;
static const std::map<MobaGame, String> values = {
{MobaGame::Dota2, "Dota2"},
{MobaGame::LeagueOfLegends, "LeagueOfLegends"},
{MobaGame::HeroesOfTheStorm, "HeroesOfTheStorm"},
{MobaGame::Smite, "Smite"},
{MobaGame::Vainglory, "Vainglory"},
{MobaGame::ArenaOfValor, "ArenaOfValor"},
{MobaGame::Paragon, "Paragon"},
{MobaGame::HeroesOfNewerth, "HeroesOfNewerth"},
};
return includeEnumName ? "MobaGame::"s + values.at(value) : values.at(value);
}
[[nodiscard]] static MobaGame Parse(const int &value)
{
using namespace Exceptions;
static const std::map<int, MobaGame> values = {
{static_cast<int>(MobaGame::Dota2), MobaGame::Dota2},
{static_cast<int>(MobaGame::LeagueOfLegends), MobaGame::LeagueOfLegends},
{static_cast<int>(MobaGame::HeroesOfTheStorm), MobaGame::HeroesOfTheStorm},
{static_cast<int>(MobaGame::Smite), MobaGame::Smite},
{static_cast<int>(MobaGame::Vainglory), MobaGame::Vainglory},
{static_cast<int>(MobaGame::ArenaOfValor), MobaGame::ArenaOfValor},
{static_cast<int>(MobaGame::Paragon), MobaGame::Paragon},
{static_cast<int>(MobaGame::HeroesOfNewerth), MobaGame::HeroesOfNewerth},
};
try
{
return values.at(value);
}
catch (...)
{
throw ParseException("MobaGame::Parse"s);
}
}
[[nodiscard]] static MobaGame Parse(const String &value)
{
using namespace Exceptions;
using namespace Extensions;
static const std::map<String, MobaGame> values = {
{"Dota2", MobaGame::Dota2},
{"LeagueOfLegends", MobaGame::LeagueOfLegends},
{"HeroesOfTheStorm", MobaGame::HeroesOfTheStorm},
{"Smite", MobaGame::Smite},
{"Vainglory", MobaGame::Vainglory},
{"ArenaOfValor", MobaGame::ArenaOfValor},
{"Paragon", MobaGame::Paragon},
{"HeroesOfNewerth", MobaGame::HeroesOfNewerth},
{"MobaGame::Dota2"s, MobaGame::Dota2},
{"MobaGame::LeagueOfLegends"s, MobaGame::LeagueOfLegends},
{"MobaGame::HeroesOfTheStorm"s, MobaGame::HeroesOfTheStorm},
{"MobaGame::Smite"s, MobaGame::Smite},
{"MobaGame::Vainglory"s, MobaGame::Vainglory},
{"MobaGame::ArenaOfValor"s, MobaGame::ArenaOfValor},
{"MobaGame::Paragon"s, MobaGame::Paragon},
{"MobaGame::HeroesOfNewerth"s, MobaGame::HeroesOfNewerth},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::Dota2)), MobaGame::Dota2},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::LeagueOfLegends)), MobaGame::LeagueOfLegends},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::HeroesOfTheStorm)), MobaGame::HeroesOfTheStorm},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::Smite)), MobaGame::Smite},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::Vainglory)), MobaGame::Vainglory},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::ArenaOfValor)), MobaGame::ArenaOfValor},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::Paragon)), MobaGame::Paragon},
{IntegralExtensions::ToString(static_cast<int>(MobaGame::HeroesOfNewerth)), MobaGame::HeroesOfNewerth},
};
try
{
return values.at(value);
}
catch (...)
{
throw ParseException("MobaGame::Parse"s);
}
}
[[nodiscard]] static std::vector<MobaGame> GetValues()
{
return {
MobaGame::Dota2,
MobaGame::LeagueOfLegends,
MobaGame::HeroesOfTheStorm,
MobaGame::Smite,
MobaGame::Vainglory,
MobaGame::ArenaOfValor,
MobaGame::Paragon,
MobaGame::HeroesOfNewerth,
};
}
};
std::ostream &operator<<(std::ostream &os, const MobaGame &value)
{
os << MobaGameEnumExtensions::ToString(value);
return os;
}
Используя это
EZNUM_ENUM_UT(MobaGame,int32_t
Dota2, EQ, 100,
LeagueOfLegends, EQ, 101,
HeroesOfTheStorm, EQ, 102,
Smite, EQ, 103,
Vainglory, EQ, 104,
ArenaOfValor, EQ, 105,
Paragon, _, _,
HeroesOfNewerth, _, _)
` Он генерирует
enum class MobaGame : int32_t
{
Dota2 = 100,
LeagueOfLegends = 101,
HeroesOfTheStorm = 102,
Smite = 103,
Vainglory = 104,
ArenaOfValor = 105,
Paragon,
HeroesOfNewerth,
};
class MobaGameEnumExtensions
{
public:
[[nodiscard]] static String ToIntegralString(const MobaGame &value)
{
using namespace Extensions;
return EnumExtensions::ToIntegralString(value);
}
[[nodiscard]] static int32_t ToIntegral(const MobaGame &value)
{
using namespace Extensions;
return EnumExtensions::ToIntegral<MobaGame>(value);
}
[[nodiscard]] static std::string ToString(const MobaGame &value, bool includeEnumName = false)
{
using namespace Extensions;
static const std::map<MobaGame, String> values = {
{MobaGame::Dota2, "Dota2"},
{MobaGame::LeagueOfLegends, "LeagueOfLegends"},
{MobaGame::HeroesOfTheStorm, "HeroesOfTheStorm"},
{MobaGame::Smite, "Smite"},
{MobaGame::Vainglory, "Vainglory"},
{MobaGame::ArenaOfValor, "ArenaOfValor"},
{MobaGame::Paragon, "Paragon"},
{MobaGame::HeroesOfNewerth, "HeroesOfNewerth"},
};
return includeEnumName ? "MobaGame::"s + values.at(value) : values.at(value);
}
[[nodiscard]] static MobaGame Parse(const int32_t &value)
{
using namespace Exceptions;
static const std::map<int32_t, MobaGame> values = {
{static_cast<int32_t>(MobaGame::Dota2), MobaGame::Dota2},
{static_cast<int32_t>(MobaGame::LeagueOfLegends), MobaGame::LeagueOfLegends},
{static_cast<int32_t>(MobaGame::HeroesOfTheStorm), MobaGame::HeroesOfTheStorm},
{static_cast<int32_t>(MobaGame::Smite), MobaGame::Smite},
{static_cast<int32_t>(MobaGame::Vainglory), MobaGame::Vainglory},
{static_cast<int32_t>(MobaGame::ArenaOfValor), MobaGame::ArenaOfValor},
{static_cast<int32_t>(MobaGame::Paragon), MobaGame::Paragon},
{static_cast<int32_t>(MobaGame::HeroesOfNewerth), MobaGame::HeroesOfNewerth},
};
try
{
return values.at(value);
}
catch (...)
{
throw ParseException("MobaGame::Parse"s);
}
}
[[nodiscard]] static MobaGame Parse(const String &value)
{
using namespace Exceptions;
using namespace Extensions;
static const std::map<String, MobaGame> values = {
{"Dota2", MobaGame::Dota2},
{"LeagueOfLegends", MobaGame::LeagueOfLegends},
{"HeroesOfTheStorm", MobaGame::HeroesOfTheStorm},
{"Smite", MobaGame::Smite},
{"Vainglory", MobaGame::Vainglory},
{"ArenaOfValor", MobaGame::ArenaOfValor},
{"Paragon", MobaGame::Paragon},
{"HeroesOfNewerth", MobaGame::HeroesOfNewerth},
{"MobaGame::Dota2"s, MobaGame::Dota2},
{"MobaGame::LeagueOfLegends"s, MobaGame::LeagueOfLegends},
{"MobaGame::HeroesOfTheStorm"s, MobaGame::HeroesOfTheStorm},
{"MobaGame::Smite"s, MobaGame::Smite},
{"MobaGame::Vainglory"s, MobaGame::Vainglory},
{"MobaGame::ArenaOfValor"s, MobaGame::ArenaOfValor},
{"MobaGame::Paragon"s, MobaGame::Paragon},
{"MobaGame::HeroesOfNewerth"s, MobaGame::HeroesOfNewerth},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::Dota2)), MobaGame::Dota2},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::LeagueOfLegends)), MobaGame::LeagueOfLegends},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::HeroesOfTheStorm)), MobaGame::HeroesOfTheStorm},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::Smite)), MobaGame::Smite},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::Vainglory)), MobaGame::Vainglory},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::ArenaOfValor)), MobaGame::ArenaOfValor},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::Paragon)), MobaGame::Paragon},
{IntegralExtensions::ToString(static_cast<int32_t>(MobaGame::HeroesOfNewerth)), MobaGame::HeroesOfNewerth},
};
try
{
return values.at(value);
}
catch (...)
{
throw ParseException("MobaGame::Parse"s);
}
}
[[nodiscard]] static std::vector<MobaGame> GetValues()
{
return {
MobaGame::Dota2,
MobaGame::LeagueOfLegends,
MobaGame::HeroesOfTheStorm,
MobaGame::Smite,
MobaGame::Vainglory,
MobaGame::ArenaOfValor,
MobaGame::Paragon,
MobaGame::HeroesOfNewerth,
};
}
};
std::ostream &operator<<(std::ostream &os, const MobaGame &value)
{
os << MobaGameEnumExtensions::ToString(value);
return os;
}
Полный код
Вы можете протестировать/запустить/любой этот код с https://github.com/JomaStackOverflowAnswers/EnumExtensionsCpp
https://godbolt.org/z/vx3YYTafh
https://replit.com/@JomaCorpFX/EnumExtensions
Сюда нельзя вставить. Слишком длинный код.
Здесь. Основная часть программы
namespace Enums
{
EZNUM_ENUM_UT(Variables, int,
X, _, _,
Y, EQ, 25,
Z, EQ, 75)
EZNUM_ENUM_UT(Fruit, int32_t,
PEAR, EQ, -100,
APPLE, _, _,
BANANA, _, _,
ORANGE, EQ, 100,
MANGO, _, _,
STRAWBERRY, EQ, 75,
WATERMELON, EQ, 100)
EZNUM_ENUM(Animal,
Dog, _, _,
Cat, _, _,
Monkey, EQ, 50,
Fish, _, _,
Human, EQ, 100,
Duck, _, _,
__COUNT, _, _)
EZNUM_ENUM_UT(MathVars32, int32_t,
X, _, _,
Y, _, _,
Z, EQ, 75)
EZNUM_ENUM_UT(MathVars64, int64_t,
X, _, _,
Y, _, _,
Z, EQ, 75)
EZNUM_ENUM(Vowels,
A, EQ, 75,
E, _, _,
I, EQ, 1500,
O, EQ, -5,
U, _, _)
EZNUM_ENUM(MobaGame,
Dota2, EQ, 100,
LeagueOfLegends, EQ, 101,
HeroesOfTheStorm, EQ, 102,
Smite, EQ, 103,
Vainglory, EQ, 104,
ArenaOfValor, EQ, 105,
Paragon, EQ, 106,
HeroesOfNewerth, EQ, -100)
}
#define PRINT_VALUES(Name) std::cout << "EnumName: "s + #Name << std::endl; \
std::cout << StringExtensions::PadRight(EMPTY_STRING , 21 + 128, '_') << std::endl; \
for (Name element : Name##EnumExtensions::GetValues()) \
{ \
std::cout << StringExtensions::PadRight(Name##EnumExtensions::ToString(element), 16) << " | " << \
StringExtensions::PadRight(Name##EnumExtensions::ToString(element, true),32) << " | " << \
StringExtensions::PadRight(Name##EnumExtensions::ToIntegralString(element),8) << " | " << \
StringExtensions::PadRight(IntegralExtensions::ToString(Name##EnumExtensions::ToIntegral(element)),8) << " | " << \
StringExtensions::PadRight(Name##EnumExtensions::ToString(Name##EnumExtensions::Parse(Name##EnumExtensions::ToString(element))),16) << " | " << \
StringExtensions::PadRight(Name##EnumExtensions::ToString(Name##EnumExtensions::Parse(Name##EnumExtensions::ToString(element, true))),16) << " | " << \
StringExtensions::PadRight(Name##EnumExtensions::ToString(Name##EnumExtensions::Parse(Name##EnumExtensions::ToIntegralString(element))),16) << " | " << \
StringExtensions::PadRight(Name##EnumExtensions::ToString(Name##EnumExtensions::Parse(Name##EnumExtensions::ToIntegral(element))),16) << std::endl; \
} \
std::cout<< std::endl;
int main() {
using namespace Enums;
using namespace Extensions;
PRINT_VALUES(Variables)
PRINT_VALUES(Fruit)
PRINT_VALUES(Animal)
PRINT_VALUES(MathVars32)
PRINT_VALUES(MathVars64)
PRINT_VALUES(Vowels)
PRINT_VALUES(MobaGame)
/* std::cout << "EnumName: "s + "MobaGame" << std::endl;
std::cout << StringExtensions::PadRight(EMPTY_STRING, 21 + 128, '_') << std::endl;
for (MobaGame element : MobaGameEnumExtensions::GetValues())
{
std::cout << StringExtensions::PadRight(MobaGameEnumExtensions::ToString(element), 16) << " | " << StringExtensions::PadRight(MobaGameEnumExtensions::ToString(element, true), 32) << " | " << StringExtensions::PadRight(MobaGameEnumExtensions::ToIntegralString(element), 8) << " | " << StringExtensions::PadRight(IntegralExtensions::ToString(MobaGameEnumExtensions::ToIntegral(element)), 8) << " | " << StringExtensions::PadRight(MobaGameEnumExtensions::ToString(MobaGameEnumExtensions::Parse(MobaGameEnumExtensions::ToString(element))), 16) << " | " << StringExtensions::PadRight(MobaGameEnumExtensions::ToString(MobaGameEnumExtensions::Parse(MobaGameEnumExtensions::ToString(element, true))), 16) << " | " << StringExtensions::PadRight(MobaGameEnumExtensions::ToString(MobaGameEnumExtensions::Parse(MobaGameEnumExtensions::ToIntegralString(element))), 16) << " | " << StringExtensions::PadRight(MobaGameEnumExtensions::ToString(MobaGameEnumExtensions::Parse(MobaGameEnumExtensions::ToIntegral(element))), 16) << std::endl;
}
std::cout << std::endl;*/
std::cin.get();
return 0;
}
Выход
VisualС++
Godbolt.org
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);