96 votes

Moyen facile d'utiliser des variables de types enum comme une chaîne de caractères en C?

Voici ce que je suis en train de faire:

typedef enum { ONE, TWO, THREE } Numbers;

Je suis en train d'écrire une fonction qui ferait un commutateur de cas semblable au suivant:

char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, num); //some way to get the symbolic constant name in here?
    } break;
    default:
      return 0; //no match
  return 1;
}

Au lieu de définir à chaque cas, est-il un moyen de définir à l'aide de l'enum variable comme je suis en train de faire ci-dessus?

73voto

Suma Points 11966

La technique de Faire quelque chose d'à la fois un C identifiant et un string? peut être utilisé ici.

Comme d'habitude avec ces préprocesseur choses, écrire et comprendre le préprocesseur partie peut être difficile, et inclut la transmission des macros pour les autres macros et implique l'utilisation de # et ## les opérateurs, mais son utilisation est très facile. Je trouve ce style très utile pour les longues énumérations, où le maintien de la même deux fois la liste peut être vraiment gênant.

Usine de code tapé une seule fois, généralement cachés dans l'en-tête:

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 GetEnumValue(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 GetEnumValue(const char *str) \
  { \
    ENUM_DEF(ENUM_STRCMP) \
    return (EnumType)0; /* handle input error */ \
  } \

Usine utilisé

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)

La technique peut être facilement étendue pour que XX macros accepte plus d'arguments, et vous pouvez aussi avoir préparé plusieurs macros pour remplacer XX pour des besoins différents, à l'image des trois que j'ai fournis dans le présent échantillon.

Comparaison de X-Macros

Tout ceci est similaire à X-Macros d'autres l'ont mentionné, je pense que cette solution est plus élégante qu'elle ne nécessite pas de #undefing de tout, ce qui vous permet de cacher des choses compliquées est dans l'usine, le fichier d'en-tête - le fichier d'en-tête est quelque chose que vous ne sont pas de toucher à tout, quand vous avez besoin de définir un nouveau protocole enum, par conséquent, de nouvelles enum définition est beaucoup plus court et plus.

61voto

Bill Forster Points 3298
// 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

16voto

sk. Points 3690

Il n'y a pas de solution intégrée. Manière la plus simple est avec un tableau de char*, où l'enum int valeur d'index à une chaîne de caractères contenant le nom descriptif de l'enum. Si vous avez un clairsemée enum (celui qui ne commence pas à 0 ou a des trous dans la numérotation) où une partie de l'int les mappages sont assez élevés pour faire un tableau de mappage impossible alors vous pouvez utiliser une table de hachage à la place.

13voto

JayG Points 1695

Il y a certainement une façon de le faire -- utiliser X() les macros. Ces macros utiliser le préprocesseur C pour construire les énumérations, les tableaux et les blocs de code à partir d'une liste de source de données. Vous avez seulement besoin d'ajouter de nouveaux éléments à la #define contenant X() macro. L'instruction switch permettrait d'étendre automatiquement.

Votre exemple peut être écrit comme suit:

 // 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;

Il y a des moyens plus efficaces (c'est à dire à l'aide de X Macros pour créer un tableau de chaînes et enum index), mais c'est le plus simple démo.

9voto

plinth Points 26817

Je sais que vous avez un couple de bonnes et solides réponses, mais connaissez-vous le # de l'opérateur dans le préprocesseur C?

Il vous permet de faire cela:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X