Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在c中将枚举名转换为字符串_C_String_Enums - Fatal编程技术网

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

如何在c中将枚举名转换为字符串,c,string,enums,C,String,Enums,是否可以将枚举数名称转换为C中的字符串?在以下情况下: enum fruit { apple, orange, grape, banana, // etc. }; 我想将其放在定义枚举的头文件中: static inline char *stringFromFruit(enum fruit f) { static const char *strings[] = { "apple", "orange", "grape", "banana", /

是否可以将枚举数名称转换为C中的字符串?

在以下情况下:

enum fruit {
    apple, 
    orange, 
    grape,
    banana,
    // etc.
};
我想将其放在定义枚举的头文件中:

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

    return strings[f];
}

没有简单的方法可以直接实现这一点。但具有允许您自动创建此类函数的宏:

 P99_DECLARE_ENUM(color, red, green, blue);
在头文件中,以及

 P99_DEFINE_ENUM(color);

在一个编译单元中(.c文件)应该完成这项工作,在该示例中,该函数将以一种方式调用
color\u getname

,使预处理器完成这项工作。它还确保枚举和字符串同步

#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)
};
预处理器完成后,您将有:

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

static const char *FRUIT_STRING[] = {
    "apple", "orange", "grape", "banana",
};
然后你可以做一些类似的事情:

printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);
如果用例实际上只是打印枚举名称,请添加以下宏:

#define str(x) #x
#define xstr(x) str(x)
然后做:

printf("enum apple as a string: %s\n", xstr(apple));
在这种情况下,两级宏似乎是多余的,但是,由于严格化在C中的工作方式,在某些情况下是必要的。例如,假设我们希望使用#define和enum:

#define foo apple

int main() {
    printf("%s\n", str(foo));
    printf("%s\n", xstr(foo));
}
产出将是:

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


有关更多信息,请参阅。

像这样的函数没有验证枚举是有点危险的。我建议使用switch语句。另一个优点是,这可用于具有已定义值的枚举,例如用于值为1、2、4、8、16等的标志

还要将所有枚举字符串放在一个数组中:-

static const char * allEnums[] = {
    "Undefined",
    "apple",
    "orange"
    /* etc */
};
定义头文件中的索引:-

#define ID_undefined       0
#define ID_fruit_apple     1
#define ID_fruit_orange    2
/* etc */
#define CASE(type,val) case val: index = ID_##type##_##val; break;
这样做可以更容易地生成不同的版本,例如,如果您想使用其他语言生成程序的国际版本

在头文件中使用宏:-

#define ID_undefined       0
#define ID_fruit_apple     1
#define ID_fruit_orange    2
/* etc */
#define CASE(type,val) case val: index = ID_##type##_##val; break;
使用switch语句创建函数时,应返回一个
const char*
,因为字符串是静态常量:-

const char * FruitString(enum fruit e){

    unsigned int index;

    switch(e){
        CASE(fruit, apple)
        CASE(fruit, orange)
        CASE(fruit, banana)
        /* etc */
        default: index = ID_undefined;
    }
    return allEnums[index];
}
如果使用Windows编程,则ID值可以是资源值

(如果使用C++,那么所有函数都可以有相同的名称。< /P>

string EnumToString(fruit e);
)我通常这样做:

#define COLOR_STR(color)                            \
    (RED       == color ? "red"    :                \
     (BLUE     == color ? "blue"   :                \
      (GREEN   == color ? "green"  :                \
       (YELLOW == color ? "yellow" : "unknown"))))   

我发现了一个C预处理器技巧,它在不声明专用数组字符串的情况下执行相同的工作(源:)

顺序枚举 随着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 };
颜色是红色。
有三种颜色

非顺序枚举 由于我希望将错误代码定义映射为数组字符串,以便将原始错误定义附加到错误代码中(例如,
“错误为3(LC\u FT\u设备未打开)。”
),因此我扩展了代码,以便您可以轻松确定各个枚举值所需的索引:

#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];
在本例中,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", };
这将导致以下实施能力:

LC_错误__字符串[-1] ==>LC\U错误\U字符串[LC\U响应\U一般错误] ==>“LC\U响应\U通用\U错误”


与Hokyo的“非顺序枚举”答案相比,更简单的替代方法是使用指示符实例化字符串数组:

#define NAMES C(RED, 10)C(GREEN, 20)C(BLUE, 30)
#define C(k, v) k = v,
enum color { NAMES };
#undef C

#define C(k, v) [v] = #k,    
const char * const color_name[] = { NAMES };

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

使用枚举和字符串数组 注意:
fruit\u str
数组中的字符串不必按照与枚举项相同的顺序声明

如何使用它 添加编译时检查 如果您害怕忘记一个字符串,可以添加以下检查:

#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);

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

就我个人而言,我看不出这有什么帮助。你能把它扩大一点,让它更明显一点吗?好的,这有什么帮助?你是说键入
enumToString(apple)
比键入
“apple”
更容易吗?在任何地方都没有任何类型的安全。除非我遗漏了一些东西,否则你在这里提出的建议毫无意义,只是成功地混淆了代码。好的,我现在明白了。在我看来,宏是假的,我建议你删除它。评论谈论宏。它在哪里?这也不方便维护。如果我插入一个新的枚举,我必须记住在数组中的正确位置复制它。这很完美,但我无法理解到底发生了什么OAlso在上述情况下如何将字符串转换为枚举?有几种方法可以实现,具体取决于您试图实现的目标?如果您不想用apple和orange污染名称空间。。。你可以在它前面加上
#define GENERATE_ENUM(ENUM)prefix###ENUM,
对于那些看到这篇文章的人来说,这种使用宏列表来枚举程序中各种项目的方法被非正式地称为“X宏”。我如何把这个库拉进来?这不是一个坏答案。它清晰、简单且易于理解。如果您在其他人需要快速阅读和理解代码的系统上工作,那么清晰性非常重要。我不建议使用预处理器技巧,除非它们在编码标准.Nice中有完整的注释或描述。这正是我寻找和使用它的目的。相同的错误:)如果索引为负(在错误枚举中非常常见),则此操作不起作用
#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);