Как преобразовать имена переименований в строку в 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]); 

Если прецедент буквально просто печатает имя enums, добавьте следующие macros:

 #define str(x) #x #define xstr(x) str(x) 

Затем выполните:

 printf("enum apple as a string: %s\n", xstr(apple)); 

В этом случае может показаться, что двухуровневый макрос лишний, однако, из-за того, как работает стробирование на C, в некоторых случаях это необходимо. Например, допустим, мы хотим использовать #define с перечислением:

 #define foo apple int main() { printf("%s\n", str(foo)); printf("%s\n", xstr(foo)); } 

Результатом будет:

 foo apple 

Это связано с тем, что str будет форматировать входной foo, а не расширять его, чтобы быть яблоком. Используя xstr, сначала выполняется макрораспределение, затем этот результат стробируется.

Дополнительную информацию см. В разделе Stringification .

В ситуации, когда у вас есть это:

 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 есть macros, которые позволяют автоматически создавать такой тип функции:

  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 ).

Последовательные enums

После изобретения Стефана Рама последовательные enums (без явного указания индекса, например enum {foo=-1, foo1 = 1} ) могут быть реализованы, как этот гениальный трюк:

 #include  #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 цвета.

Непересекающиеся enums

Поскольку я хотел сопоставить определения кодов ошибок, это строка массива, поэтому я могу добавить исходное определение ошибки в код ошибки (например, "The error is 3 (LC_FT_DEVICE_NOT_OPENED)." ), Я расширил код таким образом, чтобы вы могли легко определить требуемый индекс для соответствующих значений enums:

 #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”

Функция вроде этого без проверки enums является пустяковой опасностью. Я предлагаю использовать оператор switch. Другим преимуществом является то, что это можно использовать для перечислений, которые имеют определенные значения, например, для флагов, где значения составляют 1,2,4,8,16 и т. Д.

Также поместите все свои строки enums в один массив: –

 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; 

Сделать функцию с помощью оператора switch, это должно возвращать const char * потому что строки 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]; } 

При программировании с Windows значения ID_ могут быть значениями ресурсов.

(При использовании C ++ все функции могут иметь одно и то же имя.

 string EnumToString(fruit e); 

)

Более простая альтернатива Hokyo’s «Non-Sequential enums» отвечает на основе использования указателей для создания экземпляра массива строк:

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

Обычно я это делаю:

 #define COLOR_STR(color) \ (RED == color ? "red" : \ (BLUE == color ? "blue" : \ (GREEN == color ? "green" : \ (YELLOW == color ? "yellow" : "unknown")))) 
  • Какова механика оптимизации коротких строк в libc ++?
  • C - Разница между «char var » и «char * var»?
  • Извлечь («получить») число из строки
  • Каков тип строкового литерала в C ++?
  • Разделить строку на массив строк на основе разделителя
  • Как преобразовать строку в массив char в C ++?
  • Различия в методах сравнения строк в C #
  • Лучший способ конкатенации списка объектов String?
  • Сортировка std :: строк с номерами в них?
  • неопределенная ссылка на `strlwr '
  • Можно ли указать номера JSON?
  • Давайте будем гением компьютера.