132

使用しているいくつかのライブラリ ヘッダー ファイルに多数の列挙型があり、列挙型の値をユーザー文字列に変換する方法が必要です。その逆も同様です。

「ユーザー文字列」は列挙よりも少し読みやすくする必要があるため、RTTI はそれを行いません。

ブルート フォース ソリューションは、このような関数の束になりますが、少し C に似すぎているように感じます。

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

テンプレートを使用したエレガントなソリューションがあるという直感はありますが、まだ頭を悩ませることはできません。

更新:提案をありがとう - 列挙型がサードパーティのライブラリ ヘッダーで定義されていることを明確にする必要があるため、列挙型の定義を変更する必要はありません。

私の直感は、テンプレートを避けて、次のようにすることです。

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);
4

23 に答える 23

66

列挙名自体を文字列として使用する場合は、この投稿を参照してください。それ以外の場合、 astd::map<MyEnum, char const*>はうまく機能します。(文字列リテラルをマップ内の std::strings にコピーしても意味がありません)

追加のシンタックス シュガーについては、map_init クラスの書き方を次に示します。許可することが目標です

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

関数template <typename T> map_init(T&)は を返しますmap_init_helper<T>map_init_helper<T>T& を格納し、 trivial を定義しmap_init_helper& operator()(typename T::key_type const&, typename T::value_type const&)ます。*this(から戻ると、 のようにのoperator()連鎖が可能になります)operator()operator<<std::ostream

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

関数とヘルパー クラスはテンプレート化されているため、任意のマップまたはマップのような構造に使用できます。つまり、エントリを追加することもできますstd::unordered_map

これらのヘルパーを作成するのが気に入らない場合は、boost::assign が同じ機能をすぐに利用できます。

于 2008-10-16T10:02:47.857 に答える
33

MSaltersソリューションは優れたソリューションですが、基本的には再実装しboost::assign::map_list_ofます。ブーストがある場合は、直接使用できます。

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}
于 2008-12-04T11:10:55.050 に答える
21

別のフォームから 1 つのフォームを自動生成します。

ソース:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

生成:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

列挙値が大きい場合、生成されたフォームは unordered_map<> またはコンスタンティンによって提案されたテンプレートを使用できます。

ソース:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

生成:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

例:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}
于 2008-10-16T09:57:18.150 に答える
12

X-マクロの使用が最良の解決策であり、次のテンプレート関数を組み合わせることをお勧めします。

marcinkoziukmyopenidcomを借りて拡張する

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

color.def

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)
于 2008-11-26T14:32:38.747 に答える
11

StackOverflow の他の場所でこれに答えたことを覚えています。ここで繰り返します。基本的に、これは可変引数マクロに基づくソリューションであり、非常に使いやすいです。

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

コードで使用するには、次のようにします。

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;
于 2014-05-01T08:20:01.813 に答える
5

以下に再現するこのソリューションを使用します。

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}
于 2015-04-10T12:36:45.927 に答える
4

MyEnum 変数の文字列表現を取得したい場合、テンプレートはそれをカットしません。テンプレートは、コンパイル時に既知の整数値に特化できます。

ただし、それが必要な場合は、次を試してください。

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

これは冗長ですが、問題のようなエラーをキャッチします-あなたcase VAL1は重複しています.

于 2008-10-16T10:00:41.467 に答える
2

私はマップ m を持ちたくなるでしょう-そしてこれを列挙型に埋め込みました。

m[MyEnum.VAL1] = "Value 1"; でセットアップします。

そしてすべてが行われます。

于 2008-10-16T10:02:19.700 に答える
2

あなたの答えは、自分でいくつかのマクロを書くきっかけになりました。私の要件は次のとおりです。

  1. 列挙型の各値を一度だけ書き込むため、維持する二重リストはありません

  2. 後で #include する別のファイルに enum 値を保持しないでください。これにより、好きな場所に書き込むことができます

  3. 列挙型自体を置き換えないでください。列挙型を定義したいのですが、それに加えて、すべての列挙型名を対応する文字列にマップできるようにしたいです(レガシーコードに影響を与えないように)

  4. 検索は高速である必要があるため、これらの巨大な列挙型の場合はスイッチケースを使用しないことが望ましい

このコードは、いくつかの値を持つ従来の列挙型を作成します。さらに、各列挙値をその名前にマップする std::map として作成します (つまり、map[E_SUNDAY] = "E_SUNDAY" など)。

わかりました、ここにコードがあります:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // これは、このようなことを行う必要があるときはいつでも含めたいファイルです。そこからマクロを使用します。

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // これを使用してカスタム列挙型を作成する方法の例:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

乾杯。

于 2013-11-22T00:05:57.877 に答える
2

これは、1行のマクロコマンドのみを使用して、列挙型の << および >> ストリーム演算子を自動的に取得しようとする試みです...

定義:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

使用法:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

ただし、このスキームの制限についてはわかりません...コメントは大歓迎です!

于 2014-03-07T16:22:26.557 に答える
2

他のコードのデバッグ/分析のために、この機能を何度か必要としました。toStringこのために、いくつかのオーバーロードされたメソッドを持つクラスを生成する Perl スクリプトを作成しました。各toStringメソッドはEnum引数として を取り、 を返しますconst char*

もちろん、スクリプトは C++ 自体を列挙型として解析しませんが、シンボル テーブルを生成するために ctags を使用します。

Perl スクリプトはこちら: http://heinitz-it.de/download/enum2string/enum2string.pl.html

于 2010-03-20T23:40:14.630 に答える
1

ヘッダーで:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

.cpp ファイルで:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

警告: 不正な配列インデックスを処理しないでください。:) しかし、配列から文字列を取得する前に列挙型を検証する関数を簡単に追加できます。

于 2008-10-16T10:00:09.003 に答える
1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

上記は私の簡単な解決策です。その利点の 1 つは、メッセージ配列のサイズを制御する 'NUM' です。これにより、境界外アクセスも防止されます (賢明に使用する場合)。

文字列を取得する関数を定義することもできます。

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

私の解決策に加えて、次の解決策が非常に興味深いことがわかりました。上記の同期の問題は一般的に解決されました。

スライドはこちら: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

コードはこちら: https://github.com/arunksaha/enum_to_string

于 2014-09-24T10:33:01.763 に答える
1

マクロを使用して、この可能なエレガントなソリューションを示したかっただけです。これで問題が解決するわけではありませんが、問題について考え直す良い方法だと思います。

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

- - 編集 - -

いくつかのインターネット調査といくつかの独自の実験の後、次の解決策にたどり着きました。

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

投稿したかったのですが、誰かがこのソリューションが役立つと思うかもしれません。テンプレートクラスは必要なく、c++11 もブーストも必要ないため、単純な C にも使用できます。

---- EDIT2 ----

2 つ以上の列挙型を使用すると、情報テーブルでいくつかの問題が発生する可能性があります (コンパイラの問題)。次の回避策が機能しました。

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};
于 2012-09-14T09:52:16.167 に答える
0

最近、ベンダー ライブラリ (Fincad) で同じ問題が発生しました。幸いなことに、ベンダーはすべての列挙型の xml ドキュメントを提供しています。最終的に、列挙型ごとにマップを生成し、列挙型ごとにルックアップ関数を提供しました。この手法により、列挙型の範囲外のルックアップをインターセプトすることもできます。

swig でも似たようなことができると思いますが、Ruby で書かれたコード生成ユーティリティを喜んで提供します。

コードのサンプルを次に示します。

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

反対の方法 (文字列から列挙型ではなく、列挙型から文字列) に進みたいようですが、これは簡単に元に戻すことができます。

-ホワイト

于 2008-11-26T13:41:10.987 に答える
0

次の構文が適しているかどうかを確認してください。

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

もしそうなら、次の記事をチェックしてみてください:
http://www.gamedev.net/reference/snippets/features/cppstringizing/

于 2009-06-08T19:35:19.637 に答える