Cで列挙型の変数を文字列として使用する簡単な方法は?

何かを C 識別子と文字列の両方にするテクニック?ここで使用できます。

このようなプリプロセッサの常として、プリプロセッサ部分を記述して理解するのは難しい場合があり、マクロを他のマクロに渡したり、# および ## 演算子を使用したりする必要がありますが、使用するのは非常に簡単です。このスタイルは、同じリストを 2 回維持するのが非常に面倒な長い列挙型に非常に便利です。

工場コード - 一度だけ入力、通常はヘッダーに隠されています:

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 マクロがより多くの引数を受け入れるように簡単に拡張できます。また、このサンプルで提供した 3 つと同様に、さまざまなニーズに合わせて XX を置き換えるマクロをさらに用意することもできます。

#include / #define / #undef を使用した X-Macros との比較

これは他の人が言及した X-Macros に似ていますが、このソリューションは #undefing を何も必要としないという点でより洗練されていると思います。新しい列挙型を定義する必要があるときにまったく触れないものであるため、新しい列挙型の定義ははるかに短くてきれいです.


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

組み込みのソリューションはありません。最も簡単な方法は、 char* の配列を使用することです ここで、列挙型の int 値は、その列挙型のわかりやすい名前を含む文字列にインデックスを付けます。まばらな enum がある場合 (0 から始まらない、または番号付けにギャップがあるもの) int の一部 マッピングが十分に高く、配列ベースのマッピングが実用的でない場合は、代わりにハッシュ テーブルを使用できます。