열거 형 이름을 C에서 문자열로 변환하는 방법
C에서 열거 자 이름을 문자열로 변환 할 수 있습니까?
한 가지 방법은 전처리 기가 작업을 수행하도록하는 것입니다. 또한 열거 형과 문자열이 동기화되어 있는지 확인합니다.
#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));
이 경우 2 단계 매크로가 불필요한 것처럼 보일 수 있지만 C에서 문자열 화가 작동하는 방식으로 인해 경우에 따라 필요합니다. 예를 들어 열거 형과 함께 #define을 사용한다고 가정 해 보겠습니다.
#define foo apple
int main() {
printf("%s\n", str(foo));
printf("%s\n", xstr(foo));
}
출력은 다음과 같습니다.
foo
apple
이것은 str이 입력 foo를 apple로 확장하는 대신 문자열 화하기 때문입니다. xstr을 사용하면 매크로 확장이 먼저 수행되고 그 결과가 문자열 화됩니다.
자세한 내용은 문자열 화 를 참조하십시오.
다음과 같은 상황에서 :
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 에는 이러한 유형의 기능을 자동으로 생성 할 수있는 매크로가 있습니다.
P99_DECLARE_ENUM(color, red, green, blue);
헤더 파일에서
P99_DEFINE_ENUM(color);
한 컴파일 단위 (.c 파일)에서 트릭을 수행해야합니다.이 예제에서는 함수가 color_getname
.
전용 배열 문자열 을 선언 하지 않고 동일한 작업 을 수행하는 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 };
결과는 다음과 같습니다.
int main( void ) {
printf( "The color is %s.\n", color_name[ RED ]);
printf( "There are %d colors.\n", TOP );
}
색상은 빨간색입니다.
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];
이 예 에서 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_errors__strings [-1] ==> LC_errors__strings [LC_RESPONSE_GENERIC_ERROR] ==> "LC_RESPONSE_GENERIC_ERROR"
A function like that without validating the enum is a trifle dangerous. I suggest using a switch statement. Another advantage is that this can be used for enums that have defined values, for example for flags where the values are 1,2,4,8,16 etc.
Also put all your enum strings together in one array:-
static const char * allEnums[] = {
"Undefined",
"apple",
"orange"
/* etc */
};
define the indices in a header file:-
#define ID_undefined 0
#define ID_fruit_apple 1
#define ID_fruit_orange 2
/* etc */
Doing this makes it easier to produce different versions, for example if you want to make international versions of your program with other languages.
Using a macro, also in the header file:-
#define CASE(type,val) case val: index = ID_##type##_##val; break;
Make a function with a switch statement, this should return a const char *
because the strings static consts:-
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];
}
If programming with Windows then the ID_ values can be resource values.
(If using C++ then all the functions can have the same name.
string EnumToString(fruit e);
)
An simpler alternative to Hokyo's "Non-Sequential enums" answer, based on using designators to instantiate the string array:
#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 };
I usually do this:
#define COLOR_STR(color) \
(RED == color ? "red" : \
(BLUE == color ? "blue" : \
(GREEN == color ? "green" : \
(YELLOW == color ? "yellow" : "unknown"))))
참고URL : https://stackoverflow.com/questions/9907160/how-to-convert-enum-names-to-string-in-c
'Development Tip' 카테고리의 다른 글
Spring Boot : localhost에서 REST 컨트롤러에 액세스 할 수 없음 (404) (0) | 2020.10.06 |
---|---|
[01-12] 범위가 예상대로 작동하지 않는 이유는 무엇입니까? (0) | 2020.10.06 |
유효성을위한 포인터 테스트 (C / C ++) (0) | 2020.10.06 |
0 패딩 (선행 0)을 사용하여 int를 QString으로 변환 (0) | 2020.10.06 |
const와 const volatile의 차이점 (0) | 2020.10.06 |