如何在c中将枚举名称转换为字符串

Mis*_*sha 82 c string enums

是否有可能将枚举器名称转换为C中的字符串?

Ter*_*e M 166

一种方法是让预处理器完成工作.它还可以确保您的枚举和字符串同步.

#define FOREACH_FRUIT(FRUIT) \
        FRUIT(apple)   \
        FRUIT(orange)  \
        FRUIT(grape)   \
        FRUIT(banana)  \

#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,

enum FRUIT_ENUM {
    FOREACH_FRUIT(GENERATE_ENUM)
};

static const char *FRUIT_STRING[] = {
    FOREACH_FRUIT(GENERATE_STRING)
};
Run Code Online (Sandbox Code Playgroud)

预处理器完成后,您将拥有:

enum FRUIT_ENUM {
    apple, orange, grape, banana,
};

static const char *FRUIT_STRING[] = {
    "apple", "orange", "grape", "banana",
};
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);
Run Code Online (Sandbox Code Playgroud)

如果用例只是打印枚举名称,请添加以下宏:

#define str(x) #x
#define xstr(x) str(x)
Run Code Online (Sandbox Code Playgroud)

然后做:

printf("enum apple as a string: %s\n", xstr(apple));
Run Code Online (Sandbox Code Playgroud)

在这种情况下,似乎两级宏是多余的,但是,由于字符串化在C中的工作方式,在某些情况下是必要的.例如,假设我们想要使用带有枚举的#define:

#define foo apple

int main() {
    printf("%s\n", str(foo));
    printf("%s\n", xstr(foo));
}
Run Code Online (Sandbox Code Playgroud)

输出将是:

foo
apple
Run Code Online (Sandbox Code Playgroud)

这是因为str将字符串化输入foo而不是将其扩展为apple.通过使用xstr,首先完成宏扩展,然后将结果进行字符串化.

有关更多信息,请参阅字符串化

  • 如果您不想用苹果和橙子污染名称空间,则可以在其前面加上#define GENERATE_ENUM(ENUM)PREFIX ## ENUM, (3认同)
  • 对于看到这篇文章的人来说,这种使用宏列表枚举程序中各种项目的方法非正式地称为“X 宏”。 (3认同)
  • 这是完美的,但我无法理解到底发生了什么。:O (2认同)

Ric*_*III 23

在你有这个的情况下:

enum fruit {
    apple, 
    orange, 
    grape,
    banana,
    // etc.
};
Run Code Online (Sandbox Code Playgroud)

我喜欢把它放在枚举枚举的头文件中:

static inline char *stringFromFruit(enum fruit f)
{
    static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ };

    return strings[f];
}
Run Code Online (Sandbox Code Playgroud)

  • 对于我的生活,我看不出这有多大帮助.你可以扩大一点,使其更加明显. (4认同)
  • 好的,这有什么帮助?你是说输入`enumToString(apple)`比输入'"apple"更容易吗?它不像任何类型的安全性.除非我遗漏了你在这里建议的东西是毫无意义的,只是成功地混淆了代码. (2认同)
  • 好的,我现在看到了.宏观在我看来是虚假的,我建议你删除它. (2认同)
  • 评论谈论宏观.它在哪里? (2认同)
  • 这也很难维护.如果我插入一个新的枚举,我必须记住在数组中复制它,在正确的位置. (2认同)

Jen*_*edt 15

没有简单的方法可以直接实现这一目标.但是P99有一些宏可以让你自动创建这种类型的函数:

 P99_DECLARE_ENUM(color, red, green, blue);
Run Code Online (Sandbox Code Playgroud)

在头文件中,和

 P99_DEFINE_ENUM(color);
Run Code Online (Sandbox Code Playgroud)

然后,在一个编译单元(.c文件)中应该执行该技巧,然后将调用该函数color_getname.


jyv*_*vet 11

您不需要依赖预处理器来确保您的枚举和字符串同步。对我来说,使用宏会使代码更难阅读。

使用枚举和字符串数组

enum fruit                                                                   
{
    APPLE = 0, 
    ORANGE, 
    GRAPE,
    BANANA,
    /* etc. */
    FRUIT_MAX                                                                                                                
};   

const char * const fruit_str[] =
{
    [BANANA] = "banana",
    [ORANGE] = "orange",
    [GRAPE]  = "grape",
    [APPLE]  = "apple",
    /* etc. */  
};
Run Code Online (Sandbox Code Playgroud)

注意:fruit_str数组中的字符串不必以与枚举项相同的顺序声明。

如何使用它

printf("enum apple as a string: %s\n", fruit_str[APPLE]);
Run Code Online (Sandbox Code Playgroud)

添加编译时间检查

如果你害怕忘记一个字符串,你可以添加以下检查:

#define ASSERT_ENUM_TO_STR(sarray, max) \                                       
  typedef char assert_sizeof_##max[(sizeof(sarray)/sizeof(sarray[0]) == (max)) ? 1 : -1]

ASSERT_ENUM_TO_STR(fruit_str, FRUIT_MAX);
Run Code Online (Sandbox Code Playgroud)

如果枚举项的数量与数组中的字符串数量不匹配,将在编译时报告错误。


Mas*_*ina 10

我发现了一个C预处理器技巧,它在声明专用数组字符串的情况下执行相同的工作(来源:http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_en).

连续的枚举

继Stefan Ram发明之后,顺序枚举(没有明确说明索引,例如enum {foo=-1, foo1 = 1})可以像这个天才技巧一样实现:

#include <stdio.h>

#define NAMES C(RED)C(GREEN)C(BLUE)
#define C(x) x,
enum color { NAMES TOP };
#undef C

#define C(x) #x,    
const char * const color_name[] = { NAMES };
Run Code Online (Sandbox Code Playgroud)

这给出了以下结果:

int main( void )  { 
    printf( "The color is %s.\n", color_name[ RED ]);  
    printf( "There are %d colors.\n", TOP ); 
}
Run Code Online (Sandbox Code Playgroud)

颜色是红色.
有3种颜色.

非顺序枚举

因为我想将错误代码定义映射到数组字符串,所以我可以将原始错误定义附加到错误代码(例如"The error is 3 (LC_FT_DEVICE_NOT_OPENED)."),我以这种方式扩展代码,您可以轻松地确定相应枚举值所需的索引:

#define LOOPN(n,a) LOOP##n(a)
#define LOOPF ,
#define LOOP2(a) a LOOPF a LOOPF
#define LOOP3(a) a LOOPF a LOOPF a LOOPF
#define LOOP4(a) a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP5(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP6(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP7(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP8(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP9(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF


#define LC_ERRORS_NAMES \
    Cn(LC_RESPONSE_PLUGIN_OK, -10) \
    Cw(8) \
    Cn(LC_RESPONSE_GENERIC_ERROR, -1) \
    Cn(LC_FT_OK, 0) \
    Ci(LC_FT_INVALID_HANDLE) \
    Ci(LC_FT_DEVICE_NOT_FOUND) \
    Ci(LC_FT_DEVICE_NOT_OPENED) \
    Ci(LC_FT_IO_ERROR) \
    Ci(LC_FT_INSUFFICIENT_RESOURCES) \
    Ci(LC_FT_INVALID_PARAMETER) \
    Ci(LC_FT_INVALID_BAUD_RATE) \
    Ci(LC_FT_DEVICE_NOT_OPENED_FOR_ERASE) \
    Ci(LC_FT_DEVICE_NOT_OPENED_FOR_WRITE) \
    Ci(LC_FT_FAILED_TO_WRITE_DEVICE) \
    Ci(LC_FT_EEPROM_READ_FAILED) \
    Ci(LC_FT_EEPROM_WRITE_FAILED) \
    Ci(LC_FT_EEPROM_ERASE_FAILED) \
    Ci(LC_FT_EEPROM_NOT_PRESENT) \
    Ci(LC_FT_EEPROM_NOT_PROGRAMMED) \
    Ci(LC_FT_INVALID_ARGS) \
    Ci(LC_FT_NOT_SUPPORTED) \
    Ci(LC_FT_OTHER_ERROR) \
    Ci(LC_FT_DEVICE_LIST_NOT_READY)


#define Cn(x,y) x=y,
#define Ci(x) x,
#define Cw(x)
enum LC_errors { LC_ERRORS_NAMES TOP };
#undef Cn
#undef Ci
#undef Cw
#define Cn(x,y) #x,
#define Ci(x) #x,
#define Cw(x) LOOPN(x,"")
static const char* __LC_errors__strings[] = { LC_ERRORS_NAMES };
static const char** LC_errors__strings = &__LC_errors__strings[10];
Run Code Online (Sandbox Code Playgroud)

在此示例中,C预处理器将生成以下代码:

enum LC_errors { LC_RESPONSE_PLUGIN_OK=-10,  LC_RESPONSE_GENERIC_ERROR=-1, LC_FT_OK=0, LC_FT_INVALID_HANDLE, LC_FT_DEVICE_NOT_FOUND, LC_FT_DEVICE_NOT_OPENED, LC_FT_IO_ERROR, LC_FT_INSUFFICIENT_RESOURCES, LC_FT_INVALID_PARAMETER, LC_FT_INVALID_BAUD_RATE, LC_FT_DEVICE_NOT_OPENED_FOR_ERASE, LC_FT_DEVICE_NOT_OPENED_FOR_WRITE, LC_FT_FAILED_TO_WRITE_DEVICE, LC_FT_EEPROM_READ_FAILED, LC_FT_EEPROM_WRITE_FAILED, LC_FT_EEPROM_ERASE_FAILED, LC_FT_EEPROM_NOT_PRESENT, LC_FT_EEPROM_NOT_PROGRAMMED, LC_FT_INVALID_ARGS, LC_FT_NOT_SUPPORTED, LC_FT_OTHER_ERROR, LC_FT_DEVICE_LIST_NOT_READY, TOP };

static const char* __LC_errors__strings[] = { "LC_RESPONSE_PLUGIN_OK", "" , "" , "" , "" , "" , "" , "" , "" "LC_RESPONSE_GENERIC_ERROR", "LC_FT_OK", "LC_FT_INVALID_HANDLE", "LC_FT_DEVICE_NOT_FOUND", "LC_FT_DEVICE_NOT_OPENED", "LC_FT_IO_ERROR", "LC_FT_INSUFFICIENT_RESOURCES", "LC_FT_INVALID_PARAMETER", "LC_FT_INVALID_BAUD_RATE", "LC_FT_DEVICE_NOT_OPENED_FOR_ERASE", "LC_FT_DEVICE_NOT_OPENED_FOR_WRITE", "LC_FT_FAILED_TO_WRITE_DEVICE", "LC_FT_EEPROM_READ_FAILED", "LC_FT_EEPROM_WRITE_FAILED", "LC_FT_EEPROM_ERASE_FAILED", "LC_FT_EEPROM_NOT_PRESENT", "LC_FT_EEPROM_NOT_PROGRAMMED", "LC_FT_INVALID_ARGS", "LC_FT_NOT_SUPPORTED", "LC_FT_OTHER_ERROR", "LC_FT_DEVICE_LIST_NOT_READY", };
Run Code Online (Sandbox Code Playgroud)

这导致以下实现功能:

LC_errors__strings [-1] ==> LC_errors__strings [LC_RESPONSE_GENERIC_ERROR] ==>"LC_RESPONSE_GENERIC_ERROR"