当前位置:  开发笔记 > 后端 > 正文

在C中使用枚举类型的变量作为字符串的简单方法?

如何解决《在C中使用枚举类型的变量作为字符串的简单方法?》经验,为你挑选了7个好方法。

这是我想要做的:

typedef enum { ONE, TWO, THREE } Numbers;

我正在尝试编写一个函数来执行类似于以下的切换案例:

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;
}

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



1> Suma..:

从使某些东西成为C标识符和字符串的技术?可以在这里使用.

像往常一样使用这样的预处理器东西,编写和理解预处理器部分可能很难,并且包括将宏传递给其他宏并涉及使用#和##运算符,但使用它真的很容易.我觉得这种风格对长枚举非常有用,两次保持相同的列表真的很麻烦.

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

enumFactory.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当前的写法是编辑的结果,因为当你说服我回到那时这是x-macro,即使它可能是当时使用较少的形式(或至少有一篇文章中提到的).

2> Bill Forster..:
// 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.

3> sk...:

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



4> JayG..:

肯定有一种方法可以做到这一点 - 使用X()宏.这些宏使用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宏创建字符串数组和枚举索引),但这是最简单的演示.



5> plinth..:

我知道你有几个很好的答案,但你知道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]);
}



6> paxdiablo..:

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语句可以表示它们.这意味着你不太可能在没有另一个的情况下改变一个.



7> Samuel Danie..:

吻.您将使用您的枚举做各种其他开关/案例的事情,那么为什么打印会有所不同?如果您认为大约有100个其他地方可以忘记案件,那么忘记打印例程中的案例并不是一件大事.只需编译-Wall,它将警告非详尽的案例匹配.不要使用"默认",因为这样可以使开关彻底,并且您不会收到警告.相反,让开关退出并处理默认情况,如此...

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

推荐阅读
无名有名我无名_593
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有