文字列列挙-文字列列挙

私はゲームの分野で働いています。 この点で、常にあらゆる種類の設定を処理する必要があります。

構成に特定の列挙が必要になるたびに、ジレンマが発生します。 一方で、読みやすい設定が必要であり、他方では、このタイプの素早い解析と素早い処理が必要です。

何が欲しいですか:



"type": [ { "id": 1, "type": "one", }, { "id": 2, "type": "two", }, { "id": 6, "type": "three", } ]
      
      







しかし、同時に、次のような構造を使用したいと思います。



 enum Type { one, two, three };
      
      









さらに記事の本文では、いくつかの解決策が示されます。







ライブラリの要件は次のとおりです。







最初の解決策はc ++ 11より前でした。



 class Type { public: enum type { one, two, three }; static const std::string &to_string( type enumVal ) { static const std::map<type,std::string> 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<std::string,type> stringsEnumMap = _make_strings_enum_map(); std::map<std::string,type>::const_iterator it = stringsEnumMap.find(value); if(it==stringsEnumMap.end()) return (type)0; return it->second; } static const std::vector<type>& values() { static const std::vector<type> valueVector = _make_values(); return valueVector; } private: static const std::vector<type> _make_values() { std::vector<type> valueVector; valueVector.reserve(3); valueVector.push_back(one); valueVector.push_back(two); valueVector.push_back(three); return valueVector;\ } static std::map<type,std::string> _make_enum_strings_map() { std::map<type,std::string> 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<std::string,type> _make_strings_enum_map() { std::map<std::string,type> stringsEnumMap; stringsEnumMap.insert(std::make_pair("one", one)); stringsEnumMap.insert(std::make_pair("two", two)); stringsEnumMap.insert(std::make_pair("three", three)); return stringsEnumMap; } };
      
      





悪くはありませんが、リストごとにこれを書くのは少し長いです。



使用例:



  Type::type type; type = Type::from_string("one"); std::string stringType = Type::to_string(type);
      
      







基本的には、to_string、from_stringおよび値が呼び出されない場合に機能し、オーバーヘッドはありません。



構文STRING_ENUM(タイプ、1、2、3)に焦点を合わせることが決定されました。



c ++でそのようなコードを作成する方法を想像することはできませんが、c ++が無力である場合、それらは役立ちます。マクロです(はい、避けられない悪ですが、各クラスに60行のコードを書くのは非常に高価です)。



ここで解決策を見つけました。 わずかな変更の後、次のコードが取得されます。ほとんどのタスクでは32で十分な数の引数であると思います。



その結果、いくつかの実験の後、私はこのコードに落ち着きました:



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<Type,std::string> 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<std::string,Type> stringsEnumMap = _make_strings_enum_map(); \ std::map<std::string,Type>::const_iterator it = stringsEnumMap.find(value);\ if(it==stringsEnumMap.end())\ return (Type)0;\ return it->second;\ }\ static const std::vector<Type>& values()\ {\ static const std::vector<Type> valueVector = _make_values();\ return valueVector;\ }\ private:\ static const std::vector<Type> _make_values()\ {\ std::vector<Type> valueVector;\ valueVector.reserve(VA_SIZE(__VA_ARGS__));\ VA_FOR(ENUM_TO_VECTOR, valueVector, __VA_ARGS__)\ return valueVector;\ }\ static std::map<Type,std::string> _make_enum_strings_map()\ {\ std::map<Type,std::string> enumStringsMap;\ VA_FOR(ENUM_ENUM_TO_STRING, enumStringsMap, __VA_ARGS__)\ return enumStringsMap;\ }\ static std::map<std::string,Type> _make_strings_enum_map()\ {\ std::map<std::string,Type> stringsEnumMap;\ VA_FOR(ENUM_STRING_TO_ENUM, stringsEnumMap, __VA_ARGS__)\ return stringsEnumMap;\ }\ };
      
      







使用例:



  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 class MyEnum { ONE, TWO, THREE }; class MyEnumHelper{ public: typedef MyEnum Type; static const std::string &to_string( Type enumVal ) { static const std::map<Type, std::string> 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<std::string, Type> 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<Type>& values() { static const std::vector<Type> 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<temp.length(); ++i) temp[i] = easytolower(temp[i]); return temp; } };
      
      







使用例:



  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 <string> #include <vector> #include <map> #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<Type,std::string> enumStringsMap = { VA_FOR(ENUM_TYPE_TO_STRING, __VA_ARGS__) }; \ 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<std::string,Type> enumStringsMap = { VA_FOR(ENUM_STRING_TO_TYPE, __VA_ARGS__) }; \ auto it = enumStringsMap.find(value);\ if(it==enumStringsMap.end())\ return (Type)0;\ return it->second;\ }\ static const std::vector<Type>& values()\ {\ static const std::vector<Type> valueVector = { VA_FOR(ENUM_TYPE, __VA_ARGS__) }; \ 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<temp.length(); ++i) \ temp[i] = easytolower(temp[i]); \ return temp; \ } \ }; #endif
      
      









ユースケース:



  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



建設的な批判と合理的な提案を歓迎します。



All Articles