【问题标题】:How do I 'ToString()' an enum in C++? [duplicate]如何在 C++ 中使用“ToString()”枚举? [复制]
【发布时间】:2012-02-05 15:45:54
【问题描述】:

如何在 C++ 中使用“ToString()”枚举?

在 Java 和 C# 中,我只会调用 ToString。

enum Colours
{
    Red =0,
    Green=1,
    Blue=2
};

我需要创建一个字符串,如:“无效颜色'”+颜色+“'选择。”

【问题讨论】:

    标签: c++


    【解决方案1】:

    虽然这通常通过开关完成,但我更喜欢数组:

    #include <iostream>
    
    namespace foo {
      enum Colors { BLUE = 0, RED, GREEN, SIZE_OF_ENUM };
      static const char* ColorNames[] = { "blue", "red", "green" };
    
      // statically check that the size of ColorNames fits the number of Colors
      static_assert(sizeof(foo::ColorNames)/sizeof(char*) == foo::SIZE_OF_ENUM
        , "sizes dont match");
    } // foo
    
    int main()
    {
      std::cout << foo::ColorNames[foo::BLUE] << std::endl;
      return 0;
    }
    

    显式数组大小有利于生成编译时间 错误应该枚举的大小发生变化并且您忘记添加 合适的字符串。

    另外,Boost 保险库中有 Boost.Enum。图书馆 尚未正式发布但相当稳定并提供 你要。不过我不会向新手推荐它。

    【讨论】:

    • 忘记添加字符串也不会报错...
    • @MatthieuM。真的。构建这样的东西的唯一可靠方法可能是宏。
    • 恐怕是这样。我一直认为这也是一种耻辱,我会很感激编译器在侧面构建函数。我发现的一种方法是使用宏,尽管这或多或少是内联函数。另一种是使用测试来确保翻译适用于以前的类型(至少),然后依靠开发人员think :)
    • @MatthieuM。我想真正的解决方案仍然是使用const unordered_map。这几乎可以屏蔽维护者可能对枚举做的所有事情,并防止在运行时误用,但有点昂贵。
    • ColorNames[SIZE_OF_ENUM] 替换为ColorNames[] 并添加static_assert(sizeof(foo::ColorNames)/sizeof(char*) == foo::SIZE_OF_ENUM, "sizes dont match");
    【解决方案2】:

    用宏来个小魔术怎么样:

    #include <iostream>
    #include <string>
    #include <vector>
    
    
    // http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c
    std::vector<std::string> split(const std::string &text, char sep) {
        std::vector<std::string> tokens;
        int start = 0, end = 0;
        while ((end = text.find(sep, start)) != std::string::npos) {
            tokens.push_back(text.substr(start, end - start));
            start = end + 1;
        }
        tokens.push_back(text.substr(start));
        return tokens;
    }
    
    #define ENUM(name, ...)\
    enum name \
    {\
    __VA_ARGS__\
    };\
    std::vector<std::string> name##Map = split(#__VA_ARGS__, ',');\
        std::string toString(const name v) { return name##Map.at(v);}
    
    
    ENUM(Color, Red,Green,Blue)
    
    
    int main(int c, char**v)
    {
        std::cout << toString(Red) << toString(Blue);
        return 0;//a.exec();
    }
    

    是的,我知道这很丑,你最好不要这样做

    【讨论】:

    • 丑吗?这太棒了!
    • 我很确定这可以用 C++11 参数包更干净地完成 - 所以你在运行时根本不需要任何工作。
    • C++ 应该取消宏。
    【解决方案3】:

    这本来就是不可能的。

    C++ 枚举只是一组带有编译时名称的数字。
    在运行时,它们与普通数字无法区分。

    您需要编写一个返回字符串的switch 语句。

    【讨论】:

    • 它在 c# 中是如何工作的 .... 反射?
    • @CodingQuant:是的。您可以在Enum 类的源代码中看到反射。
    【解决方案4】:

    我真的很喜欢@Lol4t0 的宏观方法。

    我对其进行了扩展,使其也能够从字符串转换枚举:

    #include <iostream>
    #include <string>
    #include <vector>
    
    // http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c
    std::vector<std::string> split(const std::string &text, char sep) {
        std::vector<std::string> tokens;
        int start = 0, end = 0;
        while ((end = text.find(sep, start)) != std::string::npos) {
            tokens.push_back(text.substr(start, end - start));
            start = end + 1;
        }
        tokens.push_back(text.substr(start));
        return tokens;
    }
    
    #define ENUM(name, ...)\
        enum name\
        {\
            __VA_ARGS__\
        };\
        static const int name##Size = (sizeof((int[]){__VA_ARGS__})/sizeof(int));\
        static const vector<string> name##ToStringMap = split(#__VA_ARGS__, ',');\
        const string name##ToString(const name value)\
        {\
            return name##ToStringMap.at(value);\
        };\
        map<string, name> name##ToFromStringMap(...)\
        {\
            map<string, name> m;\
            name args[name##Size] = { __VA_ARGS__ };\
            \
            int i;\
            for(i = 0; i < name##Size; ++i)\
            {\
                m[name##ToString(args[i])] = args[i];\
            }\
            return m;\
        };\
        static map<string, name> name##FromStringMap = name##ToFromStringMap(__VA_ARGS__);\
        const name name##FromString(const string value, const name defaultValue)\
        {\
            if(name##FromStringMap.count(value) == 0)\
            {\
                return defaultValue;\
            }\
            return name##FromStringMap[value];\
        };
    

    用法:

    ENUM(MyEnum, Value1, Value2)
    
    void main()
    {
        string valueName = MyEnumToString(MyEnum::Value2);
        MyEnum value = MyEnumFromString(valueName, MyEnum::Value1);
    }
    

    我不是 C++ 专家,所以请告诉我您的想法或如何做得更好。

    【讨论】:

      【解决方案5】:
      enum Color
      {
          Red =0,
          Green=1,
          Blue=2
      };
      
      std::string ColorMap[] = { "Red", "Green","Blue" };
      

      使用ColorMap[c]获取字符串表示:

      std::string msg = "Invalid colour '" + ColorMap[c] + "' selected.";
      

      但是,如果 enum 的值不连续,那么您可以使用std::map 代替:

      enum Color
      {
          Red   = 0x1,
          Green = 0x2,
          Blue  = 0x4, 
          Black = 0x8, 
      };
      
      //C++11 only, as it uses std::initializer_list
      std::map<Color, std::string> ColorMap = {
          {Red, "Red"},
          {Green, "Green"},
          {Blue, "Blue"},
          {Black, "Black"}
      };
      
      //same as before!
      std::string msg = "Invalid colour '" + ColorMap[c] + "' selected.";
      

      【讨论】:

      • 在我看来是最好的解决方案,因为这种方法也可以用于包含间隙的枚举。例如。红色=0x01,绿色,蓝色=0x10,黄色。
      【解决方案6】:

      你必须手动完成,即

      const char* ToString(Colours co) {
           switch(co) {
              case Red:
                 return "Red";
              // ...
           }
      }
      

      查找表也是可能的。我还看到人们使用自定义脚本在其源代码之上生成此类内容。

      【讨论】:

        【解决方案7】:

        正如@FlopCoder 所说:

        enum Colours
        {
            Red =0,
            Green=1,
            Blue=2
        };
        char* ColourNames[] = { "Red", "Green", "Blue" };
        int colour = Green;
        printf( "Invalid colour '%s' selected.", ColourNames[ colour ] );
        

        这当然只有当您的枚举从 0 开始并且是连续的时才有效。
        @Nawaz 的方式更 C++ 时尚

        【讨论】:

        • 看看我的尺寸限制。这使成语变得更好。
        【解决方案8】:

        您可以将名称存储在由enum 值索引的字符串数组中。

        enum Colours
        {
            Red =0,
            Green=1,
            Blue=2
        };
        
        char* names[3] = {"Red", "Green", "Blue"};
        

        然后就可以打印了:"Invalid colour '" + names[colour] + "' selected."

        但如果您不按顺序定义enum 值,这种方法可能不是很有用。在那种情况下,这种方法会浪费内存。正如 Alexander Gessler 所提到的,在 enum 值上编写一个带有 switch 的函数会很有用。另一种选择可能是来自 STL 的map

        【讨论】:

          猜你喜欢
          • 2017-02-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-03
          相关资源
          最近更新 更多