

C++で、intから文字列に変換したり、文字列からintに変換できる列挙型を作りました。

[code]

    #define ENUM(name, ...) \
    class name { \
        public: \
        enum Tag {__VA_ARGS__}; \
        \
        private: \
        static std::unordered_map<int, std::string> _intToStringMap; \
        static std::unordered_map<std::string, int> _stringToIntMap; \
        public: \
        static std::string toString(int i){ \
            if (_intToStringMap.empty()) { \
                std::string args = #__VA_ARGS__; \
                createMap(_intToStringMap,_stringToIntMap,args); \
            } \
            auto it = _intToStringMap.find(i); \
            if (it != _intToStringMap.end()) { \
                return it->second; \
            } else { \
                return ""; \
            } \
        } \
        static int toInt(const std::string& str){ \
            if (_stringToIntMap.empty()) { \
                std::string args = #__VA_ARGS__; \
                createMap(_intToStringMap,_stringToIntMap,args); \
            } \
            auto it = _stringToIntMap.find(str); \
            if (it != _stringToIntMap.end()) { \
                return it->second; \
            } else { \
                return 0; \
            } \
        } \
    };
    
    void createMap(std::unordered_map<int, std::string>& intToStringMap,
                   std::unordered_map<std::string, int>& stringToIntMap,
                   const std::string& args)
    {
        std::string trimmed = ESCUtils::trim(args);
        std::vector<std::string> split = ESCUtils::split(trimmed, ",");
        int index = 0;
        for (const std::string& s : split) { \
            std::vector<std::string> vect = ESCUtils::split(s, "=");
            std::string str = vect.front();
            if (vect.size() >= 2) {
                index = ESCUtils::stoi(vect.at(1));
            }
            intToStringMap.insert(std::make_pair(index, str));
            stringToIntMap.insert(std::make_pair(str, index));
            index++;
        }
    }
    
[/code]

trimというのは空白文字、改行を取り除いた文字列を返す関数で、splitは指定した文字で文字列を分割してvectorを返す関数です。

使用例

[code]

    ENUM(RoomIndex,
         Room0=0,
         Room1,
         Room2,
         Room3,
         Shower,
         ShowerOpen,
         Toilet,
         ToiletBowl,
         ToiletTop,
         ToiletSide,
         ToiletPaper,
         Mirror,
         LeftDrawer,
         RightDrawer,
         TowelRing,
         WallPaper,
         WallHole,
         Corner,
         WashingMachine,
         Bars,
         )
    
[/code]

これを使ったところ、

[code]

    Undefined symbols for architecture arm64:
      "RoomIndex::_stringToIntMap", referenced from:
          RoomIndex::toString(int) in ESCMainGameLayer.o
      "RoomIndex::_intToStringMap", referenced from:
          RoomIndex::toString(int) in ESCMainGameLayer.o
    ld: symbol(s) not found for architecture arm64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    
[/code]

と表示されてビルドエラーになりました。  
どう直せば良いでしょうか。


CC BY-SA 3.0
2016-10-04T05:28:31.043
29341
2016-10-04T08:02:03.673
8825
question
xcode c++
intから文字列、文字列からintに変換できる列挙型


`class` 定義内に書いた `static` メンバ変数は「宣言」に留まり、それだけでは｢定義」が無いからです。

> static std::unordered_map _intToStringMap; \  
>  static std::unordered_map _stringToIntMap; \

この２つはそれだけでは定義にならないので、クラス定義外にて変数定義を与える必要があります。  
マクロの中 `class` 定義の外にて

> std::unorderd_map name::_intToStringMap; \  
>  std::unorderd_map name::_stringToIntMap; \

とすれば「とりあえず」先の `undefined symbol` は解決すると思われます。

「とりあえず」なのは下記の問題があるからです。

静的メンバ変数の定義がヘッダファイル中にあると `ODR` に反してしまいます。  
この `ENUM` マクロはヘッダファイル内にて使う事を想定しているのでしょうが、  
複数個の `***.cpp` から同一の `ENUM` マクロを使うと静的メンバ変数が複数個実体化されるので  
別のリンクエラー `duplicate symbol` が発生することになります。

うーん・・・  
`template` ベースに直すと weak symbol 化して問題なくなるかな・・・どうなんだろ？  
[template 関数で同じ関数が複数定義される場合](https://ja.stackoverflow.com/questions/14219/)  
今実装まで考えている余裕が無いので、実装は他の人にお任せと言うことで。


CC BY-SA 3.0
2016-10-04T05:57:21.550
29342
2016-10-04T05:57:21.550
2017-04-13T12:52:38.920
-1
8589
29341
answer


以下のように修正したら解決しました。

[code]

    #define ENUM(name, ...) \
    class name { \
        public: \
        enum Tag {__VA_ARGS__}; \
        \
        private: \
        static std::unordered_map<int, std::string>& getIntToStringMap(){ \
            static std::unordered_map<int, std::string> _intToStringMap; \
            return _intToStringMap; \
        } \
        static std::unordered_map<std::string, int>& getStringToIntMap(){ \
            static std::unordered_map<std::string, int> _stringToIntMap; \
            return _stringToIntMap; \
        } \
        static void createMap(const std::string& args){ \
            std::string trimmed = ESCUtils::trim(args); \
            std::vector<std::string> split = ESCUtils::split(trimmed, ","); \
            int index = 0; \
            for (const std::string& s : split) { \
                std::vector<std::string> vect = ESCUtils::split(s, "="); \
                std::string str = vect.front(); \
                if (vect.size() >= 2) { \
                    index = ESCUtils::stoi(vect.at(1)); \
                } \
                getIntToStringMap().insert(std::make_pair(index, str)); \
                getStringToIntMap().insert(std::make_pair(str, index)); \
                index++; \
            } \
        } \
        public: \
        static std::string toString(int i){ \
            if (getIntToStringMap().empty()) { \
                std::string args = #__VA_ARGS__; \
                createMap(args); \
            } \
            auto it = getIntToStringMap().find(i); \
            if (it != getIntToStringMap().end()) { \
                return it->second; \
            } else { \
                return ""; \
            } \
        } \
        static int toInt(const std::string& str){ \
            if (getStringToIntMap().empty()) { \
                std::string args = #__VA_ARGS__; \
                createMap(args); \
            } \
            auto it = getStringToIntMap().find(str); \
            if (it != getStringToIntMap().end()) { \
                return it->second; \
            } else { \
                return 0; \
            } \
        } \
    };
    
[/code]


CC BY-SA 3.0
2016-10-04T08:02:03.673
29345
2016-10-04T08:02:03.673
8825
29341
answer