【问题标题】:Easy way to use variables of enum types as string in C?在C中使用枚举类型的变量作为字符串的简单方法?
【发布时间】:2010-09-13 22:04:30
【问题描述】:

这是我想要做的:

typedef enum { ONE, TWO, THREE } Numbers;

我正在尝试编写一个函数来执行类似于以下的 switch case:

char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, num); //some way to get the symbolic constant name in here?
    } break;
    default:
      return 0; //no match
  return 1;
}

不是在每种情况下都定义,有没有办法像我在上面尝试做的那样使用枚举变量来设置它?

【问题讨论】:

    标签: c enums c-preprocessor


    【解决方案1】:

    有一种更简单、更清晰的方法 此线程中缺少的内容:

        #define ENUM_PUSH(ENUM)   ENUM,
        #define STRING_PUSH(STR)  #STR,
        
        #define FETCH_MSG(X)      \
                X(string1)        \
                X(string2)        \
        
        static const char * msgStr[] = {
            FETCH_MSG(STRING_PUSH)
        };
        
        enum msg {
            FETCH_MSG(ENUM_PUSH)
        };
        
        static enum msg message;
        
        
        void iterate(void) {
            switch (message) {
            case string1:
    // do your thing here
                break;
            case string2:
                break;
            }
        }
    

    唯一的缺点是最后一个单元格会以逗号结尾, 尽管它似乎可以被 C/C++ 编译器接受。

    【讨论】:

      【解决方案2】:

      由于所有常见的原因我不喜欢使用宏,所以我使用了一个更有限的宏解决方案,它的优点是保持枚举声明宏免费。缺点包括必须复制粘贴每个枚举的宏定义,并且在向枚举添加值时必须显式添加宏调用。

      std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs)
      {
      #define HANDLE(x) case x: os << #x; break;
          switch (cs) {
          HANDLE(CaptureState::UNUSED)
          HANDLE(CaptureState::ACTIVE)
          HANDLE(CaptureState::CLOSED)
          }
          return os;
      #undef HANDLE
      }
      

      【讨论】:

        【解决方案3】:

        没有内置解决方案。最简单的方法是使用 char* 数组,其中枚举的 int 值索引到包含该枚举的描述性名称的字符串。如果您有一个稀疏的 enum(不是从 0 开始或编号中有空白),其中一些 int 映射足够高,使得基于数组的映射不切实际,那么您可以使用哈希表而是。

        【讨论】:

        • 对此进行扩展,如果它确实是一个线性递增的列表,您可以使用编辑器的宏工具来记录每个名称并将其解析为字符串。几乎不需要额外的输入,并且您首先不需要定义。我在最后一个复制的宏上单击记录,在后面添加一个引号,然后继续到下一行的相同位置。我按停止。我按运行 X 次,不管有多少(或只是一步)。然后我可以将它包装在一个字符串数组中。
        【解决方案4】:

        Making something both a C identifier and a string? 的技术可以在这里使用。

        像往常一样使用这些预处理器的东西,编写和理解预处理器部分可能很困难,包括将宏传递给其他宏,并涉及使用 # 和 ## 运算符,但使用它真的很容易。我发现这种风格对于长枚举非常有用,在这种情况下,两次维护同一个列表真的很麻烦。

        工厂代码 - 只输入一次,通常隐藏在标题中:

        枚举工厂.h:

        // expansion macro for enum value definition
        #define ENUM_VALUE(name,assign) name assign,
        
        // expansion macro for enum to string conversion
        #define ENUM_CASE(name,assign) case name: return #name;
        
        // expansion macro for string to enum conversion
        #define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name;
        
        /// declare the access function and define enum values
        #define DECLARE_ENUM(EnumType,ENUM_DEF) \
          enum EnumType { \
            ENUM_DEF(ENUM_VALUE) \
          }; \
          const char *GetString(EnumType dummy); \
          EnumType Get##EnumType##Value(const char *string); \
        
        /// define the access function names
        #define DEFINE_ENUM(EnumType,ENUM_DEF) \
          const char *GetString(EnumType value) \
          { \
            switch(value) \
            { \
              ENUM_DEF(ENUM_CASE) \
              default: return ""; /* handle input error */ \
            } \
          } \
          EnumType Get##EnumType##Value(const char *str) \
          { \
            ENUM_DEF(ENUM_STRCMP) \
            return (EnumType)0; /* handle input error */ \
          } \
        

        工厂使用

        someEnum.h:

        #include "enumFactory.h"
        #define SOME_ENUM(XX) \
            XX(FirstValue,) \
            XX(SecondValue,) \
            XX(SomeOtherValue,=50) \
            XX(OneMoreValue,=100) \
        
        DECLARE_ENUM(SomeEnum,SOME_ENUM)
        

        someEnum.cpp:

        #include "someEnum.h"
        DEFINE_ENUM(SomeEnum,SOME_ENUM)
        

        该技术可以轻松扩展,使 XX 宏接受更多参数,并且您还可以根据不同需要准备更多宏来替代 XX,类似于我在此示例中提供的三个。

        使用#include / #define / #undef 与 X-Macros 进行比较

        虽然这类似于其他人提到的 X-Macros,但我认为这个解决方案更优雅,因为它不需要 #undefing 任何东西,它允许您隐藏更多复杂的东西在工厂的头文件中 -当你需要定义一个新的枚举时,头文件是你根本不会触及的,因此新的枚举定义更短更清晰。

        【讨论】:

        • 我不知道你怎么能说这比 x-macros 更好/更差 - 这 is x-macros。 SOME_ENUM(XX) 正是一个 X-macro(准确地说,是传递 XX 函数而不是使用 #def #undef 的“用户表单”),然后依次将整个 X-MACRO 传递给DEFINE_ENUM 使用它。不要从解决方案中拿走任何东西 - 它运作良好。只是为了澄清这是对 X 宏的使用。
        • @BeeOnRope 您注意到的差异很重要,并将此解决方案与 idiomatic X 宏(例如 Wikipedia's examples)区分开来。传递XX 优于 re-#defineing 的优点是前一种模式可以用于宏扩展。请注意,唯一与此一样简洁的其他解决方案都需要创建和多次包含一个单独的文件来定义一个新的枚举。
        • 另一个技巧是使用枚举名称作为宏名称。您可以简单地写#define DEFINE_ENUM(EnumType) ...,将ENUM_DEF(...) 替换为EnumType(...),然后让用户说#define SomeEnum(XX) ...。 C 预处理器将根据上下文将SomeEnum 扩展为宏调用,然后是括号,否则扩展为常规标记。 (当然,如果用户喜欢使用SomeEnum(2) 转换为枚举类型而不是(SomeEnum)2static_cast&lt;SomeEnum&gt;(2),这会导致问题。)
        • @pmttavara - 当然,如果快速搜索有任何迹象表明 x-macros 最常见的用法是使用固定的内部宏名称以及 #define#undef。尽管“用户表单”(例如,在this article 的底部建议)是一种 x 宏,您是否不同意?我当然也一直将它称为 x 宏,并且在我最近使用的 C 代码库中,它是最常见的形式(这显然是有偏见的观察)。不过,我可能一直在解析 OP 错误。
        • @BeeOnRope 当前的措辞是编辑的结果,正如您当时说服我的那样,这是 x-macro,即使它可能是较少使用的形式(或至少在文章中较少提及)那么。
        【解决方案5】:

        通过在这里合并一些技术,我想出了最简单的形式:

        #define MACROSTR(k) #k
        
        #define X_NUMBERS \
               X(kZero  ) \
               X(kOne   ) \
               X(kTwo   ) \
               X(kThree ) \
               X(kFour  ) \
               X(kMax   )
        
        enum {
        #define X(Enum)       Enum,
            X_NUMBERS
        #undef X
        } kConst;
        
        static char *kConstStr[] = {
        #define X(String) MACROSTR(String),
            X_NUMBERS
        #undef X
        };
        
        int main(void)
        {
            int k;
            printf("Hello World!\n\n");
        
            for (k = 0; k < kMax; k++)
            {
                printf("%s\n", kConstStr[k]);
            }
        
            return 0;
        }
        

        【讨论】:

          【解决方案6】:

          我认为像 Boost.Fusion 这样的用于调整结构和类的解决方案会很好,他们甚至在某个时候拥有它,将枚举用作融合序列。

          所以我只制作了一些小宏来生成代码来打印枚举。这并不完美,与 Boost.Fusion 生成的样板代码无关,但可以像 Boost Fusion 宏一样使用。我真的想生成 Boost.Fusion 所需的类型以集成到允许打印结构成员名称的基础设施中,但这将在稍后发生,现在这只是宏:

          #ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
          #define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
          
          #include <swissarmyknife/detail/config.hpp>
          
          #include <string>
          #include <ostream>
          #include <boost/preprocessor/cat.hpp>
          #include <boost/preprocessor/stringize.hpp>
          #include <boost/preprocessor/seq/for_each.hpp>
          
          
          #define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C(                     \
              R, unused, ENUMERATION_ENTRY)                                               \
              case ENUMERATION_ENTRY:                                                     \
                return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY);                             \
              break;                                                                      
          
          /**
           * \brief Adapts ENUM to reflectable types.
           *
           * \param ENUM_TYPE To be adapted
           * \param ENUMERATION_SEQ Sequence of enum states
           */
          #define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ)                   \
              inline std::string to_string(const ENUM_TYPE& enum_value) {                 \
                switch (enum_value) {                                                     \
                BOOST_PP_SEQ_FOR_EACH(                                                    \
                    SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C,                   \
                    unused, ENUMERATION_SEQ)                                              \
                  default:                                                                \
                    return BOOST_PP_STRINGIZE(ENUM_TYPE);                                 \
                }                                                                         \
              }                                                                           \
                                                                                          \
              inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
                os << to_string(value);                                                   \
                return os;                                                                \
              }
          
          #endif
          

          下面的旧答案很糟糕,请不要使用它。 :)

          旧答案:

          我一直在寻找一种无需过多更改枚举声明语法即可解决此问题的方法。我找到了一个解决方案,它使用预处理器从字符串化枚举声明中检索字符串。

          我可以像这样定义非稀疏枚举:

          SMART_ENUM(State, 
              enum State {
                  RUNNING,
                  SLEEPING, 
                  FAULT, 
                  UNKNOWN
              })
          

          我可以通过不同的方式与他们互动:

          // With a stringstream
          std::stringstream ss;
          ss << State::FAULT;
          std::string myEnumStr = ss.str();
          
          //Directly to stdout
          std::cout << State::FAULT << std::endl;
          
          //to a string
          std::string myStr = State::to_string(State::FAULT);
          
          //from a string
          State::State myEnumVal = State::from_string(State::FAULT);
          

          基于以下定义:

          #define SMART_ENUM(enumTypeArg, ...)                                                     \
          namespace enumTypeArg {                                                                  \
              __VA_ARGS__;                                                                         \
              std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) {                 \
                      os << swissarmyknife::enums::to_string(#__VA_ARGS__, val);                   \
                      return os;                                                                   \
              }                                                                                    \
                                                                                               \
              std::string to_string(const enumTypeArg& val) {                                      \
                      return swissarmyknife::enums::to_string(#__VA_ARGS__, val);                  \
              }                                                                                    \
                                                                                               \
              enumTypeArg from_string(const std::string &str) {                                    \
                      return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str);   \
              }                                                                                    \
          }                                                                                        \
          
          
          namespace swissarmyknife { namespace enums {
          
              static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
                  size_t begin = completeEnumDeclaration.find_first_of('{');
                  size_t end = completeEnumDeclaration.find_last_of('}');
                  const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
          
                  size_t count = 0;
                  size_t found = 0;
                  do {
                      found = identifiers.find_first_of(",}", found+1);
          
                      if (enumVal == count) {
                          std::string identifiersSubset = identifiers.substr(0, found);
                          size_t beginId = identifiersSubset.find_last_of("{,");
                          identifiersSubset = identifiersSubset.substr(beginId+1);
                          boost::algorithm::trim(identifiersSubset);
                          return identifiersSubset;
                      }
          
                      ++count;
                  } while (found != std::string::npos);
          
                  throw std::runtime_error("The enum declaration provided doesn't contains this state.");
              }                                                  
          
              template <typename EnumType>
              static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
                  size_t begin = completeEnumDeclaration.find_first_of('{');
                  size_t end = completeEnumDeclaration.find_last_of('}');
                  const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
          
                  size_t count = 0;
                  size_t found = 0;
                  do {
                      found = identifiers.find_first_of(",}", found+1);
          
                      std::string identifiersSubset = identifiers.substr(0, found);
                      size_t beginId = identifiersSubset.find_last_of("{,");
                      identifiersSubset = identifiersSubset.substr(beginId+1);
                      boost::algorithm::trim(identifiersSubset);
          
                      if (identifiersSubset == enumStr) {
                          return static_cast<EnumType>(count);
                      }
          
                      ++count;
                  } while (found != std::string::npos);
          
                  throw std::runtime_error("No valid enum value for the provided string");
              }                      
          
          }}
          

          当我需要支持稀疏枚举并且有更多时间时,我将使用 boost::xpressive 改进 to_stringfrom_string 实现,但是这将花费编译时间,因为执行了重要的模板并且生成的可执行文件可能真的更大。但这样做的好处是它比这个丑陋的手动字符串操作代码更具可读性和可维护性。 :D

          否则我总是使用 boost::bimap 来执行枚举值和字符串之间的这种映射,但它必须手动维护。

          【讨论】:

            【解决方案7】:

            如果你使用 gcc,可以使用:

            const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};
            

            那就打个比方吧

            enum_to_string_map[enum1]
            

            【讨论】:

              【解决方案8】:

              这是一个使用具有以下功能的宏的解决方案:

              1. 枚举的每个值只写一次,所以不需要维护双重列表

              2. 不要将枚举值保存在稍后#included 的单独文件中,这样我可以将其写入任何我想要的地方

              3. 不要替换枚举本身,我仍然希望定义枚举类型,但除此之外我希望能够将每个枚举名称映射到相应的字符串(不影响遗留代码)

              4. 搜索应该很快,所以对于那些巨大的枚举最好不要使用 switch-case

              https://stackoverflow.com/a/20134475/1812866

              【讨论】:

                【解决方案9】:

                我创建了一个简单的模板类streamable_enum,它使用流运算符&lt;&lt;&gt;&gt;,并基于std::map&lt;Enum, std::string&gt;

                #ifndef STREAMABLE_ENUM_HPP
                #define STREAMABLE_ENUM_HPP
                
                #include <iostream>
                #include <string>
                #include <map>
                
                template <typename E>
                class streamable_enum
                {
                public:
                    typedef typename std::map<E, std::string> tostr_map_t;
                    typedef typename std::map<std::string, E> fromstr_map_t;
                
                    streamable_enum()
                    {}
                
                    streamable_enum(E val) :
                        Val_(val)
                    {}
                
                    operator E() {
                        return Val_;
                    }
                
                    bool operator==(const streamable_enum<E>& e) {
                        return this->Val_ == e.Val_;
                    }
                
                    bool operator==(const E& e) {
                        return this->Val_ == e;
                    }
                
                    static const tostr_map_t& to_string_map() {
                        static tostr_map_t to_str_(get_enum_strings<E>());
                        return to_str_;
                    }
                
                    static const fromstr_map_t& from_string_map() {
                        static fromstr_map_t from_str_(reverse_map(to_string_map()));
                        return from_str_;
                    }
                private:
                    E Val_;
                
                    static fromstr_map_t reverse_map(const tostr_map_t& eToS) {
                        fromstr_map_t sToE;
                        for (auto pr : eToS) {
                            sToE.emplace(pr.second, pr.first);
                        }
                        return sToE;
                    }
                };
                
                template <typename E>
                streamable_enum<E> stream_enum(E e) {
                    return streamable_enum<E>(e);
                }
                
                template <typename E>
                typename streamable_enum<E>::tostr_map_t get_enum_strings() {
                    // \todo throw an appropriate exception or display compile error/warning
                    return {};
                }
                
                template <typename E>
                std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
                    auto& mp = streamable_enum<E>::to_string_map();
                    auto res = mp.find(e);
                    if (res != mp.end()) {
                        os << res->second;
                    } else {
                        os.setstate(std::ios_base::failbit);
                    }
                    return os;
                }
                
                template <typename E>
                std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
                    std::string str;
                    is >> str;
                    if (str.empty()) {
                        is.setstate(std::ios_base::failbit);
                    }
                    auto& mp = streamable_enum<E>::from_string_map();
                    auto res = mp.find(str);
                    if (res != mp.end()) {
                        e = res->second;
                    } else {
                        is.setstate(std::ios_base::failbit);
                    }
                    return is;
                }
                
                #endif
                

                用法:

                #include "streamable_enum.hpp"
                
                using std::cout;
                using std::cin;
                using std::endl;
                
                enum Animal {
                    CAT,
                    DOG,
                    TIGER,
                    RABBIT
                };
                
                template <>
                streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
                    return {
                        { CAT, "Cat"},
                        { DOG, "Dog" },
                        { TIGER, "Tiger" },
                        { RABBIT, "Rabbit" }
                    };
                }
                
                int main(int argc, char* argv []) {
                    cout << "What animal do you want to buy? Our offering:" << endl;
                    for (auto pr : streamable_enum<Animal>::to_string_map()) {          // Use from_string_map() and pr.first instead
                        cout << " " << pr.second << endl;                               // to have them sorted in alphabetical order
                    }
                    streamable_enum<Animal> anim;
                    cin >> anim;
                    if (!cin) {
                        cout << "We don't have such animal here." << endl;
                    } else if (anim == Animal::TIGER) {
                        cout << stream_enum(Animal::TIGER) << " was a joke..." << endl;
                    } else {
                        cout << "Here you are!" << endl;
                    }
                
                    return 0;
                }
                

                【讨论】:

                  【解决方案10】:
                  #define stringify( name ) # name
                  
                  enum MyEnum {
                      ENUMVAL1
                  };
                  ...stuff...
                  
                  stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1
                  

                  Further discussion on this method

                  Preprocessor directive tricks for newcomers

                  【讨论】:

                    【解决方案11】:

                    boost::preprocessor 的使用使一个优雅的解决方案成为可能,如下所示:

                    第一步:包含头文件:

                    #include "EnumUtilities.h"
                    

                    第二步:使用以下语法声明枚举对象:

                    MakeEnum( TestData,
                             (x)
                             (y)
                             (z)
                             );
                    

                    第 3 步:使用您的数据:

                    获取元素个数:

                    td::cout << "Number of Elements: " << TestDataCount << std::endl;
                    

                    获取相关字符串:

                    std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
                    std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
                    std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;
                    

                    从关联字符串中获取枚举值:

                    std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
                    std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
                    std::cout << "Value of z is " << TestData2Enum("z") << std::endl;
                    

                    这看起来干净紧凑,没有额外的文件要包含。 我在 EnumUtilities.h 中编写的代码如下:

                    #include <boost/preprocessor/seq/for_each.hpp>
                    #include <string>
                    
                    #define REALLY_MAKE_STRING(x) #x
                    #define MAKE_STRING(x) REALLY_MAKE_STRING(x)
                    #define MACRO1(r, data, elem) elem,
                    #define MACRO1_STRING(r, data, elem)    case elem: return REALLY_MAKE_STRING(elem);
                    #define MACRO1_ENUM(r, data, elem)      if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;
                    
                    
                    #define MakeEnum(eName, SEQ) \
                        enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
                        last_##eName##_enum}; \
                        const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
                        static std::string eName##2String(const enum eName eel) \
                        { \
                            switch (eel) \
                            { \
                            BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
                            default: return "Unknown enumerator value."; \
                            }; \
                        }; \
                        static enum eName eName##2Enum(const std::string eStrEl) \
                        { \
                            BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
                            return (enum eName)0; \
                        };
                    

                    有一些限制,即 boost::preprocessor 的限制。在这种情况下,常量列表不能超过 64 个元素。

                    按照同样的逻辑,你也可以考虑创建稀疏枚举:

                    #define EnumName(Tuple)                 BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
                    #define EnumValue(Tuple)                BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
                    #define MACRO2(r, data, elem)           EnumName(elem) EnumValue(elem),
                    #define MACRO2_STRING(r, data, elem)    case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));
                    
                    #define MakeEnumEx(eName, SEQ) \
                        enum eName { \
                        BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
                        last_##eName##_enum }; \
                        const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
                        static std::string eName##2String(const enum eName eel) \
                        { \
                            switch (eel) \
                            { \
                            BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
                            default: return "Unknown enumerator value."; \
                            }; \
                        };  
                    

                    在这种情况下,语法是:

                    MakeEnumEx(TestEnum,
                               ((x,))
                               ((y,=1000))
                               ((z,))
                               );
                    

                    用法与上面类似(减去 eName##2Enum 函数,您可以尝试从前面的语法推断)。

                    我在 mac 和 linux 上对其进行了测试,但请注意 boost::preprocessor 可能不是完全可移植的。

                    【讨论】:

                      【解决方案12】:

                      我知道你有几个很好的可靠答案,但你知道 C 预处理器中的 # 运算符吗?

                      它可以让你这样做:

                      #define MACROSTR(k) #k
                      
                      typedef enum {
                          kZero,
                          kOne,
                          kTwo,
                          kThree
                      } kConst;
                      
                      static char *kConstStr[] = {
                          MACROSTR(kZero),
                          MACROSTR(kOne),
                          MACROSTR(kTwo),
                          MACROSTR(kThree)
                      };
                      
                      static void kConstPrinter(kConst k)
                      {
                          printf("%s", kConstStr[k]);
                      }
                      

                      【讨论】:

                      • char const *kConstStr[]
                      【解决方案13】:

                      亲吻。你将用你的枚举做各种其他的 switch/case 事情,那么为什么打印应该不同呢?当您考虑到大约有 100 个其他地方可以忘记一个案例时,在您的打印程序中忘记一个案例并不是什么大问题。只需编译 -Wall,它将警告非详尽的大小写匹配。不要使用“默认”,因为这会使切换变得详尽,并且您不会收到警告。相反,让开关退出并像这样处理默认情况......

                      const char *myenum_str(myenum e)
                      {
                          switch(e) {
                          case ONE: return "one";
                          case TWO: return "two";
                          }
                          return "invalid";
                      }
                      

                      【讨论】:

                        【解决方案14】:

                        肯定有办法做到这一点——使用X() macros。这些宏使用 C 预处理器从源数据列表中构造枚举、数组和代码块。您只需将新项目添加到包含 X() 宏的#define。 switch 语句会自动展开。

                        你的例子可以写成如下:

                         // Source data -- Enum, String
                         #define X_NUMBERS \
                            X(ONE,   "one") \
                            X(TWO,   "two") \
                            X(THREE, "three")
                        
                         ...
                        
                         // Use preprocessor to create the Enum
                         typedef enum {
                          #define X(Enum, String)       Enum,
                           X_NUMBERS
                          #undef X
                         } Numbers;
                        
                         ...
                        
                         // Use Preprocessor to expand data into switch statement cases
                         switch(num)
                         {
                         #define X(Enum, String) \
                             case Enum:  strcpy(num_str, String); break;
                         X_NUMBERS
                         #undef X
                        
                             default: return 0; break;
                         }
                         return 1;
                        

                        还有更高效的方法(即使用 X Macros 创建字符串数组和枚举索引),但这是最简单的演示。

                        【讨论】:

                          【解决方案15】:
                          【解决方案16】:
                          // Define your enumeration like this (in say numbers.h);
                          ENUM_BEGIN( Numbers )
                              ENUM(ONE),
                              ENUM(TWO),
                              ENUM(FOUR)
                          ENUM_END( Numbers )
                          
                          // The macros are defined in a more fundamental .h file (say defs.h);
                          #define ENUM_BEGIN(typ) enum typ {
                          #define ENUM(nam) nam
                          #define ENUM_END(typ) };
                          
                          // Now in one and only one .c file, redefine the ENUM macros and reinclude
                          //  the numbers.h file to build a string table
                          #undef ENUM_BEGIN
                          #undef ENUM
                          #undef ENUM_END
                          #define ENUM_BEGIN(typ) const char * typ ## _name_table [] = {
                          #define ENUM(nam) #nam
                          #define ENUM_END(typ) };
                          #undef NUMBERS_H_INCLUDED   // whatever you need to do to enable reinclusion
                          #include "numbers.h"
                          
                          // Now you can do exactly what you want to do, with no retyping, and for any
                          //  number of enumerated types defined with the ENUM macro family
                          //  Your code follows;
                          char num_str[10];
                          int process_numbers_str(Numbers num) {
                            switch(num) {
                              case ONE:
                              case TWO:
                              case THREE:
                              {
                                strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO"
                              } break;
                              default:
                                return 0; //no match
                            return 1;
                          }
                          
                          // Sweet no ? After being frustrated by this for years, I finally came up
                          //  with this solution for my most recent project and plan to reuse the idea
                          //  forever
                          

                          【讨论】:

                          • 这就是 cpp 的用途。 +1。
                          • 这是一个很好的答案,它似乎是不使用特殊工具可以做到的最好的事情,我以前做过这种事情;但它仍然从来没有真正感觉“正确”,我从来没有真正喜欢这样做......
                          • 小改动:#define ENUM_END(typ) }; extern const char * typ ## _name_table[]; in defs.h 文件 - 这将在您使用的文件中声明您的名称表。 (不过,我想不出一个声明表格大小的好方法。)另外,我个人会省略最后的分号,但无论哪种方式,优点都是值得商榷的。
                          • @Bill,为什么要在#define ENUM_END(typ) }; 行中使用typ
                          • 这在我希望我的宏定义为“ONE = 5”的地方不起作用
                          【解决方案17】:

                          查看Mu Dynamics Research Labs - Blog Archive 的想法。我在今年早些时候发现了这个——我忘记了我遇到它的确切上下文——并将它改编成这段代码。我们可以讨论在前面添加 E 的优点;它适用于解决的具体问题,但不是一般解决方案的一部分。我把它藏在我的“vignettes”文件夹中——在那里我保留了一些有趣的代码片段,以防我以后想要它们。我很尴尬地说我当时没有记下这个想法的来源。

                          标题:paste1.h

                          /*
                          @(#)File:           $RCSfile: paste1.h,v $
                          @(#)Version:        $Revision: 1.1 $
                          @(#)Last changed:   $Date: 2008/05/17 21:38:05 $
                          @(#)Purpose:        Automated Token Pasting
                          */
                          
                          #ifndef JLSS_ID_PASTE_H
                          #define JLSS_ID_PASTE_H
                          
                          /*
                           * Common case when someone just includes this file.  In this case,
                           * they just get the various E* tokens as good old enums.
                           */
                          #if !defined(ETYPE)
                          #define ETYPE(val, desc) E##val,
                          #define ETYPE_ENUM
                          enum {
                          #endif /* ETYPE */
                          
                             ETYPE(PERM,  "Operation not permitted")
                             ETYPE(NOENT, "No such file or directory")
                             ETYPE(SRCH,  "No such process")
                             ETYPE(INTR,  "Interrupted system call")
                             ETYPE(IO,    "I/O error")
                             ETYPE(NXIO,  "No such device or address")
                             ETYPE(2BIG,  "Arg list too long")
                          
                          /*
                           * Close up the enum block in the common case of someone including
                           * this file.
                           */
                          #if defined(ETYPE_ENUM)
                          #undef ETYPE_ENUM
                          #undef ETYPE
                          ETYPE_MAX
                          };
                          #endif /* ETYPE_ENUM */
                          
                          #endif /* JLSS_ID_PASTE_H */
                          

                          示例来源:

                          /*
                          @(#)File:           $RCSfile: paste1.c,v $
                          @(#)Version:        $Revision: 1.2 $
                          @(#)Last changed:   $Date: 2008/06/24 01:03:38 $
                          @(#)Purpose:        Automated Token Pasting
                          */
                          
                          #include "paste1.h"
                          
                          static const char *sys_errlist_internal[] = {
                          #undef JLSS_ID_PASTE_H
                          #define ETYPE(val, desc) desc,
                          #include "paste1.h"
                              0
                          #undef ETYPE
                          };
                          
                          static const char *xerror(int err)
                          {
                              if (err >= ETYPE_MAX || err <= 0)
                                  return "Unknown error";
                              return sys_errlist_internal[err];
                          }
                          
                          static const char*errlist_mnemonics[] = {
                          #undef JLSS_ID_PASTE_H
                          #define ETYPE(val, desc) [E ## val] = "E" #val,
                          #include "paste1.h"
                          #undef ETYPE
                          };
                          
                          #include <stdio.h>
                          
                          int main(void)
                          {
                              int i;
                          
                              for (i = 0; i < ETYPE_MAX; i++)
                              {
                                  printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i));
                              }
                              return(0);
                          }
                          

                          不一定是世界上对 C 预处理器最干净的使用 - 但它确实可以防止多次写出材料。

                          【讨论】:

                            【解决方案18】:

                            C 或 C++ 不提供此功能,尽管我经常需要它。

                            以下代码有效,尽管它最适合非稀疏枚举。

                            typedef enum { ONE, TWO, THREE } Numbers;
                            char *strNumbers[] = {"one","two","three"};
                            printf ("Value for TWO is %s\n",strNumbers[TWO]);
                            

                            我所说的非稀疏,我的意思是不是形式

                            typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;
                            

                            因为它有很大的差距。

                            这种方法的优点是枚举和字符串的定义彼此靠近;在函数中使用 switch 语句将它们分开。这意味着您不太可能在没有其他的情况下更改其中一个。

                            【讨论】:

                              【解决方案19】:

                              试试Converting C++ enums to stringscomments 的改进可以解决枚举项具有任意值时的问题。

                              【讨论】:

                                【解决方案20】:

                                如果枚举索引是从 0 开始的,您可以将名称放入 char* 数组中,并使用枚举值对其进行索引。

                                【讨论】:

                                  猜你喜欢
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2019-11-06
                                  • 1970-01-01
                                  • 2011-07-02
                                  • 2013-01-23
                                  • 2014-07-23
                                  相关资源
                                  最近更新 更多