Простой способ использовать переменные перечислимых типов как строка в C?

86
задан Brian Tompsett - 汤莱恩 1 February 2016 в 19:30
поделиться

11 ответов

Нет никакого встроенного решения. Самый легкий путь с массивом char* где международные индексы стоимости перечисления к строке, содержащей описательное имя того перечисления. Если у Вас есть редкое enum (тот, который не запускается в 0 или имеет разрывы в нумерации), где некоторые из эти int отображения достаточно высоки для создания основанного на массиве отображения непрактичным тогда, Вы могли использовать хэш-таблицу вместо этого.

14
ответ дан roottraveller 24 November 2019 в 07:59
поделиться

Если перечислимый индекс на основе 0, можно поставить имена в массиве символа* и индексировать их с перечислением значений.

0
ответ дан Colen 24 November 2019 в 07:59
поделиться

Проверьте идеи в научно-исследовательские лаборатории 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 - но это действительно предотвращает выписывание материала многократно.

1
ответ дан Jonathan Leffler 24 November 2019 в 07:59
поделиться

Попробуйте перечисления C++ Преобразования к строкам . комментарии имеют улучшения, которые решают проблему, когда перечислимые объекты имеют произвольные значения.

4
ответ дан Bob Nadler 24 November 2019 в 07:59
поделиться

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;

, так как это имеет огромные разрывы в ней.

преимущество этого метода состоит в том, что он поместил определения перечислений и строк друг около друга; наличие оператора переключения в функции разделяет их. Это означает, что Вы, менее вероятно, изменитесь один без другого.

6
ответ дан paxdiablo 24 November 2019 в 07:59
поделиться

Я знаю, что у Вас есть пара хороших основательных ответов, но Вы знаете о # операторе в препроцессоре 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]);
}
8
ответ дан Peter Hosey 24 November 2019 в 07:59
поделиться

Существует определенно способ сделать это - использование 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 Макросов для создания массива строк и перечислимого индекса), но это - самая простая демонстрация.

13
ответ дан JayG 24 November 2019 в 07:59
поделиться
// 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
61
ответ дан Bill Forster 24 November 2019 в 07:59
поделиться

Техника от Создание чего-то и идентификатор C и строка? может использоваться здесь.

, Как обычно, с таким материалом препроцессора, пишущий и понимая часть препроцессора может быть твердым, и включает передающие макросы в другие макросы и включает использование # и ## операторы, но использование его очень легко. Я нахожу этот стиль очень полезным для длинных перечислений, где ведение того же списка дважды может быть действительно неприятным.

код Factory - ввел только однажды, обычно скрытый в заголовке:

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 */ \
  } \

Factory использовал

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-макросами с помощью #include / #define / #undef

, В то время как это подобно другим X-макросов, упомянул, я думаю, что это решение более изящно в этом, это не требует #undefing ничего, которое позволяет, Вы для сокрытия большего количества сложного материала находитесь на фабрике заголовочный файл - заголовочный файл - что-то, что Вы не заходите во все, когда необходимо определить новое перечисление, поэтому новое перечислимое определение является намного короче и более чистым.

65
ответ дан Community 24 November 2019 в 07:59
поделиться

ПОЦЕЛУЙ. Вы будете делать с вашими перечислениями множество других вещей, связанных с переключателями / регистрами, так почему же печать должна быть другой? Забыть футляр в рутине печати - не такая уж большая проблема, если учесть, что существует около 100 других мест, где вы можете забыть футляр. Просто скомпилируйте -Wall, который предупредит о неполном совпадении регистра. Не используйте «по умолчанию», потому что это сделает переключение исчерпывающим и вы не получите предупреждений. Вместо этого позвольте переключателю выйти и разобраться со случаем по умолчанию следующим образом ...

const char *myenum_str(myenum e)
{
    switch(e) {
    case ONE: return "one";
    case TWO: return "two";
    }
    return "invalid";
}
6
ответ дан 24 November 2019 в 07:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: