[Из песочницы] String enum — строковые enum
Я работаю в игровой сфере. В связи с этим постоянно приходится сталкиваться со всевозможными конфигами.Каждый раз, когда в конфигах должно быть некое перечисление, возникает дилема. С одной стороны, хочется читаемых конфигов, с другой — быстрого парсинга и быстрого обращения по этому типу.Что хочется: «type»: [ { «id»: 1, «type»: «one», }, { «id»: 2, «type»: «two», }, { «id»: 6, «type»: «three», } ] Но в тоже время в кодe хочется использовать структуры типа:
enum Type { one, two, three }; Требования к библиотере были следующие:
Кроссплатформенность; Минимум зависимостей; Скорость чтения; Простой синтаксис; Первый вариант решения был до с++11:
class Type
{
public:
enum type { one, two, three };
static const std: string &to_string (type enumVal)
{
static const std: map
Type: type type; type = Type: from_string («one»); std: string stringType = Type: to_string (type); В принципе, работает, если to_string, from_string и values не вызываются, то никаких накладных расходов не будет.
Решено было остановится на синтаксисе STRING_ENUM (Type, one, two, three);
Как сделать подобный код на c++ я не представляю, но там, где бессилен c++, помогут Они — макросы (да, неизбежное зло, но писать 60 строк кода для каждого класса очень накладно).
Нашел кусочек решения тут. После небольших доработок получается следующий код, думаю, что 32 — достаточное количество аргументов для большинства задач.
В итоге, после некоторых экспериментов остановился на таком коде:
#define VA_SIZE (…) INVOKE (VA_GET_SIZE VA_OB INVOKE (VA_SPEC##__VA_ARGS__()), 0, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 VA_CB)
#define VA_OB ( #define VA_CB) #define VA_SPEC () 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34 #define VA_GET_SIZE (_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_, n,…) n
#define INVOKE (…) INVOKE_A (__VA_ARGS__) #define INVOKE_A (…) __VA_ARGS__
#define VA_FOR (macro, data,…) INVOKE (CAT (VA_FOR, VA_SIZE (__VA_ARGS__)) (macro, data, VA_APPLY (VA_FIRST (__VA_ARGS__)), (VA_APPLY (VA_WO_FIRST (__VA_ARGS__)))))
#define VA_APPLY (x) x #define VA_FIRST (a, …) a #define VA_WO_FIRST (a, …) __VA_ARGS__
#define VA_FOR0(m, d, e, x) #define VA_FOR1(m, d, e, x) m (d, e) #define VA_FOR2(m, d, e, x) m (d, e) VA_FOR1(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR3(m, d, e, x) m (d, e) VA_FOR2(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR4(m, d, e, x) m (d, e) VA_FOR3(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR5(m, d, e, x) m (d, e) VA_FOR4(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR6(m, d, e, x) m (d, e) VA_FOR5(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR7(m, d, e, x) m (d, e) VA_FOR6(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR8(m, d, e, x) m (d, e) VA_FOR7(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR9(m, d, e, x) m (d, e) VA_FOR8(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR10(m, d, e, x) m (d, e) VA_FOR9(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR11(m, d, e, x) m (d, e) VA_FOR10(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR12(m, d, e, x) m (d, e) VA_FOR11(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR13(m, d, e, x) m (d, e) VA_FOR12(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR14(m, d, e, x) m (d, e) VA_FOR13(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR15(m, d, e, x) m (d, e) VA_FOR14(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR16(m, d, e, x) m (d, e) VA_FOR15(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR17(m, d, e, x) m (d, e) VA_FOR16(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR18(m, d, e, x) m (d, e) VA_FOR17(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR19(m, d, e, x) m (d, e) VA_FOR18(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR20(m, d, e, x) m (d, e) VA_FOR19(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR21(m, d, e, x) m (d, e) VA_FOR20(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR22(m, d, e, x) m (d, e) VA_FOR21(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR23(m, d, e, x) m (d, e) VA_FOR22(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR24(m, d, e, x) m (d, e) VA_FOR23(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR25(m, d, e, x) m (d, e) VA_FOR24(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR26(m, d, e, x) m (d, e) VA_FOR25(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR27(m, d, e, x) m (d, e) VA_FOR26(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR28(m, d, e, x) m (d, e) VA_FOR27(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR29(m, d, e, x) m (d, e) VA_FOR28(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR30(m, d, e, x) m (d, e) VA_FOR29(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR31(m, d, e, x) m (d, e) VA_FOR30(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x))) #define VA_FOR32(m, d, e, x) m (d, e) VA_FOR31(m, d, VA_APPLY (VA_FIRST x), (VA_APPLY (VA_WO_FIRST x)))
#define CAT (x, y) CAT_A (x, y) #define CAT_A (x, y) x##y
#define ENUM_IDENTITY (X, A) A, #define ENUM_STRING_TO_ENUM (X, A) X.insert (std: make_pair (#A, A)); #define ENUM_ENUM_TO_STRING (X, A) X.insert (std: make_pair (A,#A)); #define ENUM_TO_VECTOR (X, A) X.push_back (A);
#define STRING_ENUM (name, …) \
class name \
{ \
public:\
enum Type { VA_FOR (ENUM_IDENTITY, fake, __VA_ARGS__) }; \
static const std: string &to_string (Type enumVal) \
{\
static const std: map
Пример использования:
STRING_ENUM (MyStringEnum, one, two, three); MyStringEnum: Type type; type = MyStringEnum: from_string («one»); std: string stringType = MyStringEnum: to_string (type); Плюсы решения компилируется почти во всех компиляторах, лично пробовал на gcc 4.3–4.9 clang 2.8–3.4 MSVC 2012–2013.
Прошло некоторое время и c++11 стал поддерживаться почти всеми компиляторами. Захотелось сделать типобезопасный enum, также решено было вынести вспомогательные ф-ии в отдельный класс, так как enum class глобальную область видимости не засоряет:
enum class MyEnum { ONE, TWO, THREE };
class MyEnumHelper{
public:
typedef MyEnum Type;
static const std: string &to_string (Type enumVal)
{
static const std: map
Type: Type type;
type = Type: from_string («one»);
std: string stringType = Type: to_string (type);
Также в процессе адаптации было принято решение разрешить назначать конкретное значение определенному значению. Идея была подсмотрена тут. И, наконец, после многих часов отладки макросов препроцессора (помогла эта тема), вот он, финальльный вариант:
#ifndef _StringEnums_h
#define _StringEnums_h #include #define VA_SIZE (…) VA_SIZE_((VA_SIZE_PREFIX_ ## __VA_ARGS__ ## _VA_SIZE_POSTFIX,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
#define VA_SIZE_INVOKE (…) INVOKE (VA_SIZE (__VA_ARGS__))
#define VA_SIZE_(__args) VA_GET_SIZE __args #define VA_SIZE_PREFIX__VA_SIZE_POSTFIX,,,0 #define VA_GET_SIZE (__p0,__p1,__p2,__p3,__p4,__p5,__p6,__p7,__p8,__p9,__p10,__p11,__p12,__p13,__p14,__p15,__p16,__p17,__p18,__p19,__p20,__p21,__p22,__p23,__p24,__p25,__p26,__p27,__p28,__p29,__p30,__p31,__n,…) __n #define INVOKE (…) INVOKE_A (__VA_ARGS__)
#define INVOKE_A (…) __VA_ARGS__ #define VA_FOR (macro,…) INVOKE (CAT (VA_FOR, VA_SIZE_INVOKE (__VA_ARGS__)) (macro, (__VA_ARGS__))) #define VA_APPLY (x) x
#define VA_FIRST (a, …) a
#define VA_WO_FIRST (a, …) __VA_ARGS__ #define VA_FOR0(m, x)
#define VA_FOR1(m, x) m (VA_APPLY (VA_FIRST x))
#define VA_FOR2(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR1(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR3(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR2(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR4(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR3(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR5(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR4(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR6(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR5(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR7(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR6(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR8(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR7(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR9(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR8(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR10(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR9(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR11(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR10(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR12(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR11(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR13(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR12(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR14(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR13(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR15(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR14(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR16(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR15(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR17(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR16(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR18(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR17(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR19(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR18(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR20(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR19(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR21(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR20(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR22(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR21(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR23(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR22(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR24(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR23(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR25(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR24(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR26(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR25(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR27(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR26(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR28(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR27(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR29(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR28(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR30(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR29(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR31(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR30(m, (VA_APPLY (VA_WO_FIRST x)))
#define VA_FOR32(m, x) m (VA_APPLY (VA_FIRST x)) VA_FOR31(m, (VA_APPLY (VA_WO_FIRST x))) #define CAT (x, y) CAT_A (x, y)
#define CAT_A (x, y) x##y #define M_STR (A) M_STR_(A)
#define M_STR_(A) #A #define M_LOWER_STR (A) M_LOWER_STR_(A)
#define M_LOWER_STR_(A) tolower (#A) #define M_IF (P, T, E) CAT (M_IF_, P)(T, E)
#define M_IF_1(T, E) E
#define M_IF_2(T, E) T
#define M_FIRST (A, …) A
#define M_SECOND (A, B, …) B
#define M_ID (…) __VA_ARGS__ #define ENUM_ENAME (A) M_IF (VA_SIZE (M_ID A), M_FIRST A = M_SECOND A, A),
#define ENUM_ELEM (A) M_IF (VA_SIZE (M_ID A), M_FIRST A, A)
#define ENUM_ELEM_TYPE (A) Type: ENUM_ELEM (A)
#define ENUM_ELEM_NAME (A) M_LOWER_STR (ENUM_ELEM (A))
#define ENUM_STRING_TO_TYPE (A) {ENUM_ELEM_NAME (A), ENUM_ELEM_TYPE (A)},
#define ENUM_TYPE_TO_STRING (A) {ENUM_ELEM_TYPE (A), ENUM_ELEM_NAME (A)},
#define ENUM_TYPE (A) ENUM_ELEM_TYPE (A), #define STRING_ENUM (name, …) \
enum class name { VA_FOR (ENUM_ENAME, __VA_ARGS__) }; \
class name##Helper { \
public: \
typedef name Type; \
static const std: string &to_string (Type enumVal) \
{\
static const std: map #endif
Вариант использования:
STRING_ENUM (MyStringEnum, one, (two,4), three);
MyStringEnum: Type type;
type = MyStringEnum: from_string («one»);
std: string stringType = MyStringEnum: to_string (type);
Проверял на gcc 4.6–4.9 clang 3.2–3.3 MSVC 2013. Конструктивная критика и рациональные предложения приветствуются.