Нет никакого встроенного решения. Самый легкий путь с массивом char*
где международные индексы стоимости перечисления к строке, содержащей описательное имя того перечисления. Если у Вас есть редкое enum
(тот, который не запускается в 0 или имеет разрывы в нумерации), где некоторые из эти int
отображения достаточно высоки для создания основанного на массиве отображения непрактичным тогда, Вы могли использовать хэш-таблицу вместо этого.
Если перечислимый индекс на основе 0, можно поставить имена в массиве символа* и индексировать их с перечислением значений.
Проверьте идеи в научно-исследовательские лаборатории Mu Dynamics - Архив Блога . Я нашел это ранее в этом году - я забываю точный контекст, где я столкнулся с ним - и адаптировал его в этот код. Мы можем обсудить достоинства добавить E в передней стороне; это применимо к определенной решенной проблеме, но не часть общего решения. Я спрятал это в своей папке 'виньеток' - где я сохраняю интересные фрагменты кода в случае, если я хочу их позже. Я смущен, чтобы сказать, что я не сохранял примечание того, куда эта идея прибыла из в то время.
Заголовок: источник paste1.h
/*
@(#)File: $RCSfile: paste1.h,v $
@(#)Version: $Revision: 1.1 $
@(#)Last changed: $Date: 2008/05/17 21:38:05 $
@(#)Purpose: Automated Token Pasting
*/
#ifndef JLSS_ID_PASTE_H
#define JLSS_ID_PASTE_H
/*
* Common case when someone just includes this file. In this case,
* they just get the various E* tokens as good old enums.
*/
#if !defined(ETYPE)
#define ETYPE(val, desc) E##val,
#define ETYPE_ENUM
enum {
#endif /* ETYPE */
ETYPE(PERM, "Operation not permitted")
ETYPE(NOENT, "No such file or directory")
ETYPE(SRCH, "No such process")
ETYPE(INTR, "Interrupted system call")
ETYPE(IO, "I/O error")
ETYPE(NXIO, "No such device or address")
ETYPE(2BIG, "Arg list too long")
/*
* Close up the enum block in the common case of someone including
* this file.
*/
#if defined(ETYPE_ENUM)
#undef ETYPE_ENUM
#undef ETYPE
ETYPE_MAX
};
#endif /* ETYPE_ENUM */
#endif /* JLSS_ID_PASTE_H */
В качестве примера:
/*
@(#)File: $RCSfile: paste1.c,v $
@(#)Version: $Revision: 1.2 $
@(#)Last changed: $Date: 2008/06/24 01:03:38 $
@(#)Purpose: Automated Token Pasting
*/
#include "paste1.h"
static const char *sys_errlist_internal[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) desc,
#include "paste1.h"
0
#undef ETYPE
};
static const char *xerror(int err)
{
if (err >= ETYPE_MAX || err <= 0)
return "Unknown error";
return sys_errlist_internal[err];
}
static const char*errlist_mnemonics[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) [E ## val] = "E" #val,
#include "paste1.h"
#undef ETYPE
};
#include <stdio.h>
int main(void)
{
int i;
for (i = 0; i < ETYPE_MAX; i++)
{
printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i));
}
return(0);
}
Не обязательно самое чистое использование в мире препроцессора C - но это действительно предотвращает выписывание материала многократно.
Попробуйте перечисления C++ Преобразования к строкам . комментарии имеют улучшения, которые решают проблему, когда перечислимые объекты имеют произвольные значения.
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;
, так как это имеет огромные разрывы в ней.
преимущество этого метода состоит в том, что он поместил определения перечислений и строк друг около друга; наличие оператора переключения в функции разделяет их. Это означает, что Вы, менее вероятно, изменитесь один без другого.
Я знаю, что у Вас есть пара хороших основательных ответов, но Вы знаете о # операторе в препроцессоре 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]);
}
Существует определенно способ сделать это - использование X () макросы . Они использование макросов препроцессор C для построения перечислений, массивов и блоков кода из списка исходных данных. Только необходимо добавить новые объекты к #define, содержащему X () макрос. Оператор переключения расширился бы автоматически.
Ваш пример может быть записан следующим образом:
// 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 Макросов для создания массива строк и перечислимого индекса), но это - самая простая демонстрация.
// 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
Техника от Создание чего-то и идентификатор 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 для различных потребностей, подобных трем, которые я обеспечил в этом образце.
, В то время как это подобно другим X-макросов, упомянул, я думаю, что это решение более изящно в этом, это не требует #undefing ничего, которое позволяет, Вы для сокрытия большего количества сложного материала находитесь на фабрике заголовочный файл - заголовочный файл - что-то, что Вы не заходите во все, когда необходимо определить новое перечисление, поэтому новое перечислимое определение является намного короче и более чистым.
ПОЦЕЛУЙ. Вы будете делать с вашими перечислениями множество других вещей, связанных с переключателями / регистрами, так почему же печать должна быть другой? Забыть футляр в рутине печати - не такая уж большая проблема, если учесть, что существует около 100 других мест, где вы можете забыть футляр. Просто скомпилируйте -Wall, который предупредит о неполном совпадении регистра. Не используйте «по умолчанию», потому что это сделает переключение исчерпывающим и вы не получите предупреждений. Вместо этого позвольте переключателю выйти и разобраться со случаем по умолчанию следующим образом ...
const char *myenum_str(myenum e)
{
switch(e) {
case ONE: return "one";
case TWO: return "two";
}
return "invalid";
}