String enum - string enum
I work in the gaming field. In this regard, constantly have to deal with all kinds of configs.
Every time there should be a certain enumeration in the configs, a dilemma arises. On the one hand, I want readable configs, on the other hand, quick parsing and quick handling of this type.
What do you want:
But at the same time, I want to use structures like:
Further in the body of the article several solutions will be given.
The library requirements were as follows:
The first solution was before c ++ 11:
Not bad, but writing this for each listing is a bit long.
Usage example:
Basically, it works, if to_string, from_string and values are not called, then there will be no overhead.
It was decided to focus on the syntax STRING_ENUM (Type, one, two, three);
I can not imagine how to make such code in c ++, but where c ++ is powerless, They will help. They are macros (yes, an inevitable evil, but writing 60 lines of code for each class is very expensive).
Found a piece of solution here . After minor modifications, the following code is obtained, I think that 32 is a sufficient number of arguments for most tasks.
As a result, after some experiments, I settled on this code:
Usage example:
Pros of the solution is compiled in almost all compilers, I personally tried on gcc 4.3-4.9 clang 2.8-3.4 MSVC 2012-2013.
Some time passed and c ++ 11 became supported by almost all compilers. I wanted to make type-safe enum, it was also decided to put auxiliary functions into a separate class, since the enum class does not clog the global scope:
Every time there should be a certain enumeration in the configs, a dilemma arises. On the one hand, I want readable configs, on the other hand, quick parsing and quick handling of this type.
What do you want:
"type":
[
{
"id": 1,
"type": "one",
},
{
"id": 2,
"type": "two",
},
{
"id": 6,
"type": "three",
}
]
But at the same time, I want to use structures like:
enum Type
{
one,
two,
three
};
Further in the body of the article several solutions will be given.
The library requirements were as follows:
- Cross-platform;
- Minimum dependencies;
- Reading speed;
- Simple syntax
The first solution was before c ++ 11:
class Type
{
public:
enum type { one, two, three };
static const std::string &to_string( type enumVal )
{
static const std::map enumStringsMap = _make_enum_strings_map();
auto it = enumStringsMap.find(enumVal);
static std::string emptyString;
if(it==enumStringsMap.end())
return emptyString;
return it->second;
}
static type from_string(const std::string &value)
{
static const std::map stringsEnumMap = _make_strings_enum_map();
std::map::const_iterator it = stringsEnumMap.find(value);
if(it==stringsEnumMap.end())
return (type)0;
return it->second;
}
static const std::vector& values()
{
static const std::vector valueVector = _make_values();
return valueVector;
}
private:
static const std::vector _make_values()
{
std::vector valueVector;
valueVector.reserve(3);
valueVector.push_back(one);
valueVector.push_back(two);
valueVector.push_back(three);
return valueVector;\
}
static std::map _make_enum_strings_map()
{
std::map enumStringsMap;
enumStringsMap.insert(std::make_pair(one, "one"));
enumStringsMap.insert(std::make_pair(two, "two"));
enumStringsMap.insert(std::make_pair(three, "three"));
return enumStringsMap;
}
static std::map _make_strings_enum_map()
{
std::map stringsEnumMap;
stringsEnumMap.insert(std::make_pair("one", one));
stringsEnumMap.insert(std::make_pair("two", two));
stringsEnumMap.insert(std::make_pair("three", three));
return stringsEnumMap;
}
};
Not bad, but writing this for each listing is a bit long.
Usage example:
Type::type type;
type = Type::from_string("one");
std::string stringType = Type::to_string(type);
Basically, it works, if to_string, from_string and values are not called, then there will be no overhead.
It was decided to focus on the syntax STRING_ENUM (Type, one, two, three);
I can not imagine how to make such code in c ++, but where c ++ is powerless, They will help. They are macros (yes, an inevitable evil, but writing 60 lines of code for each class is very expensive).
Found a piece of solution here . After minor modifications, the following code is obtained, I think that 32 is a sufficient number of arguments for most tasks.
As a result, after some experiments, I settled on this code:
string_enum.h
#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 enumStringsMap = _make_enum_strings_map();\
auto it = enumStringsMap.find(enumVal);\
static std::string emptyString;\
if(it==enumStringsMap.end())\
return emptyString;\
return it->second;\
}\
static Type from_string(const std::string &value)\
{\
static const std::map stringsEnumMap = _make_strings_enum_map(); \
std::map::const_iterator it = stringsEnumMap.find(value);\
if(it==stringsEnumMap.end())\
return (Type)0;\
return it->second;\
}\
static const std::vector& values()\
{\
static const std::vector valueVector = _make_values();\
return valueVector;\
}\
private:\
static const std::vector _make_values()\
{\
std::vector valueVector;\
valueVector.reserve(VA_SIZE(__VA_ARGS__));\
VA_FOR(ENUM_TO_VECTOR, valueVector, __VA_ARGS__)\
return valueVector;\
}\
static std::map _make_enum_strings_map()\
{\
std::map enumStringsMap;\
VA_FOR(ENUM_ENUM_TO_STRING, enumStringsMap, __VA_ARGS__)\
return enumStringsMap;\
}\
static std::map _make_strings_enum_map()\
{\
std::map stringsEnumMap;\
VA_FOR(ENUM_STRING_TO_ENUM, stringsEnumMap, __VA_ARGS__)\
return stringsEnumMap;\
}\
};
Usage example:
STRING_ENUM( MyStringEnum, one, two, three);
MyStringEnum::Type type;
type = MyStringEnum::from_string("one");
std::string stringType = MyStringEnum::to_string(type);
Pros of the solution is compiled in almost all compilers, I personally tried on gcc 4.3-4.9 clang 2.8-3.4 MSVC 2012-2013.
Some time passed and c ++ 11 became supported by almost all compilers. I wanted to make type-safe enum, it was also decided to put auxiliary functions into a separate class, since the enum class does not clog the global scope:
enum class MyEnum { ONE, TWO, THREE };
class MyEnumHelper{
public:
typedef MyEnum Type;
static const std::string &to_string( Type enumVal )
{
static const std::map enumStringsMap = { { Type::ONE, tolower("ONE") }, { Type::TWO, tolower("TWO") }, { Type::THREE, tolower("THREE") } };
auto it = enumStringsMap.find(enumVal);
static std::string emptyString;
if(it==enumStringsMap.end())
return emptyString;
return it->second;
}
static Type from_string(const std::string &value)
{
static const std::map enumStringsMap = { { tolower("ONE"), Type::ONE }, { tolower("TWO"), Type::TWO }, { tolower("THREE"), Type::THREE } };
auto it = enumStringsMap.find(value);
if(it==enumStringsMap.end())
return (Type)0;
return it->second;
}
static const std::vector& values()
{
static const std::vector valueVector = { Type::ONE, Type::TWO, Type::THREE };
return valueVector;
}
private:
inline static char easytolower(char in){
if(in<='Z' && in>='A')
return in-('Z'-'z');
return in;
}
static std::string tolower(std::string &&tolower)
{
std::string temp = tolower;
for (std::string::size_type i=0; i
Пример использования:
Type::Type type;
type = Type::from_string("one");
std::string stringType = Type::to_string(type);
Также в процессе адаптации было принято решение разрешить назначать конкретное значение определенному значению. Идея была подсмотрена тут.
И, наконец, после многих часов отладки макросов препроцессора (помогла эта тема), вот он, финальльный вариант:
string_enum.h#ifndef _StringEnums_h
#define _StringEnums_h
#include
#include
#include
Вариант использования:
STRING_ENUM( MyStringEnum, ONE, (TWO,4), THREE);
MyStringEnum type;
type = MyStringEnumHelper::from_string("one");
std::string stringType = MyStringEnumHelper::to_string(type);
Проверял на gcc 4.6-4.9 clang 3.2-3.3 MSVC 2013.
UPD пример относительно скорости http://ideone.com/1HNGIe
Конструктивная критика и рациональные предложения приветствуются.