130

名前付き列挙型があるとします。

enum MyEnum {
      FOO,
      BAR = 0x50
};

私がグーグルで検索したのは、プロジェクト内のすべてのヘッダーをスキャンし、列挙型ごとに 1 つの関数を含むヘッダーを生成するスクリプト (任意の言語) です。

char* enum_to_string(MyEnum t);

そして、次のような実装:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

問題は、実際には型定義された列挙型と名前のない C スタイルの列挙型にあります。誰もこれについて何か知っていますか?

編集:生成された関数を除いて、ソリューションは私のソースを変更すべきではありません。列挙型は API に含まれているため、これまでに提案されたソリューションを使用することは選択肢ではありません。

4

34 に答える 34

74

X マクロは最適なソリューションです。例:

#include <iostream>

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

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c];
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

colors.def:

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

ただし、文字列を少し調整できるように、通常は次の方法を好みます。

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.
于 2008-10-14T16:00:21.290 に答える
50

GCCXMLをチェックアウトすることをお勧めします。

サンプル コードで GCCXML を実行すると、以下が生成されます。

<GCC_XML>
  <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
  <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
  <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
    <EnumValue name="FOO" init="0"/>
    <EnumValue name="BAR" init="80"/>
  </Enumeration>
  <File id="f0" name="my_enum.h"/>
</GCC_XML>

Enumeration および EnumValue タグを引き出して、目的のコードを生成するために、任意の言語を使用できます。

于 2008-10-14T16:03:32.313 に答える
46

@hydroo: 追加ファイルなし:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)

#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
    SOME_ENUM(MAKE_ENUM)
};

#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
    SOME_ENUM(MAKE_STRINGS)
};
于 2008-10-26T15:47:02.753 に答える
36

私がよく行うのは、名前が列挙値と同じ順序と位置にある C 配列を作成することです。

例えば。

enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };

次に、人間が読める値が必要な場所で配列を使用できます。

colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];

状況によっては、文字列化演算子 (プリプロセッサ リファレンスの # を参照) を少し試してみることができます。たとえば、次のような場合です。

#define printword(XX) cout << #XX;
printword(red);

「赤」を標準出力に出力します。残念ながら、変数に対しては機能しません (変数名が出力されるため)。

于 2008-10-14T15:32:39.130 に答える
8

QT は (メタ オブジェクト コンパイラのおかげで) それを引き出すことができます:

QNetworkReply::NetworkError error;

error = fetchStuff();

if (error != QNetworkReply::NoError) {

    QString errorValue;

    QMetaObject meta = QNetworkReply::staticMetaObject;

    for (int i=0; i < meta.enumeratorCount(); ++i) {

        QMetaEnum m = meta.enumerator(i);

        if (m.name() == QLatin1String("NetworkError")) {

            errorValue = QLatin1String(m.valueToKey(error));

            break;

        }

    }

    QMessageBox box(QMessageBox::Information, "Failed to fetch",

                "Fetching stuff failed with error '%1`").arg(errorValue),

                QMessageBox::Ok);

    box.exec();

    return 1;

}

Qt では、Q_OBJECT マクロを持つすべてのクラスが、QMetaObject 型の静的メンバー「staticMetaObject」を自動的に持ちます。その後、プロパティ、シグナル、スロット、実際の列挙型など、あらゆる種類のクールなものを見つけることができます。

ソース

于 2008-10-14T18:01:08.190 に答える
8

今日、この車輪を再発明したので、共有したいと思いました。

この実装では、定数を定義するコードを変更する必要はありませ#define。これは、列挙型、 s、または整数に発展するその他のものです。私の場合、他のシンボルに関して定義されたシンボルがありました。また、スパース値でもうまく機能します。同じ値に対して複数の名前を使用することもでき、常に最初の名前が返されます。唯一の欠点は、定数のテーブルを作成する必要があることです。たとえば、新しい定数が追加されると、定数が古くなる可能性があります。

struct IdAndName
{
   int          id;
   const char * name;
   bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
};
#define ID_AND_NAME(x) { x, #x }

const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
{
   if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
      std::stable_sort(table_begin, table_end);

   IdAndName searchee = { id, NULL };
   IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
   return (p == table_end || p->id != id) ? NULL : p->name;
}

template<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

使用方法の例:

static IdAndName WindowsErrorTable[] =
{
   ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
   ID_AND_NAME(NO_ERROR),
   ID_AND_NAME(ERROR_INVALID_FUNCTION),
   ID_AND_NAME(ERROR_FILE_NOT_FOUND),
   ID_AND_NAME(ERROR_PATH_NOT_FOUND),
   ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
   ID_AND_NAME(ERROR_ACCESS_DENIED),
   ID_AND_NAME(ERROR_INVALID_HANDLE),
   ID_AND_NAME(ERROR_ARENA_TRASHED),
   ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
   ID_AND_NAME(ERROR_INVALID_BLOCK),
   ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
   ID_AND_NAME(ERROR_BAD_FORMAT),
   ID_AND_NAME(ERROR_INVALID_ACCESS),
   ID_AND_NAME(ERROR_INVALID_DATA),
   ID_AND_NAME(ERROR_INVALID_DRIVE),
   ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
   ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
   ID_AND_NAME(ERROR_NO_MORE_FILES)
};

const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

この関数は、テーブルをソートする必要があるクイック ルックアップを実行するためにIdToName依存しています。std::lower_boundテーブルの最初の 2 つのエントリが順不同である場合、関数は自動的に並べ替えます。

編集:コメントにより、同じ原則を使用する別の方法を考えさせられました。マクロは、大きなswitchステートメントの生成を簡素化します。

#define ID_AND_NAME(x) case x: return #x

const char * WindowsErrorToName(int id)
{
    switch(id)
    {
        ID_AND_NAME(ERROR_INVALID_FUNCTION);
        ID_AND_NAME(ERROR_FILE_NOT_FOUND);
        ID_AND_NAME(ERROR_PATH_NOT_FOUND);
        ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
        ID_AND_NAME(ERROR_ACCESS_DENIED);
        ID_AND_NAME(ERROR_INVALID_HANDLE);
        ID_AND_NAME(ERROR_ARENA_TRASHED);
        ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
        ID_AND_NAME(ERROR_INVALID_BLOCK);
        ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
        ID_AND_NAME(ERROR_BAD_FORMAT);
        ID_AND_NAME(ERROR_INVALID_ACCESS);
        ID_AND_NAME(ERROR_INVALID_DATA);
        ID_AND_NAME(ERROR_INVALID_DRIVE);
        ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
        ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
        ID_AND_NAME(ERROR_NO_MORE_FILES);
        default: return NULL;
    }
}
于 2012-07-20T19:50:25.193 に答える
7

これは C++11 で実行できます

#include <map>
enum MyEnum { AA, BB, CC, DD };

static std::map< MyEnum, const char * > info = {
   {AA, "This is an apple"},
   {BB, "This is a book"},
   {CC, "This is a coffee"},
   {DD, "This is a door"}
};

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}
于 2014-06-18T22:57:23.910 に答える
6
#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

この方法に関するさらなる議論

初心者向けのプリプロセッサ ディレクティブのトリック

于 2012-11-02T02:44:46.760 に答える
5

方法の数を見るのは興味深い。これが私がずっと前に使ったものです:

myenummap.hファイル内:

#include <map>
#include <string>
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map<unsigned int, std::string>
{
  mymap()
  {
    this->operator[]( one ) = "ONE";
    this->operator[]( two ) = "TWO";
    this->operator[]( three ) = "THREE";
    this->operator[]( five ) = "FIVE";
    this->operator[]( six ) = "SIX";
    this->operator[]( seven ) = "SEVEN";
  };
  ~mymap(){};
};

main.cppで

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

constではありませんが、便利です。

C++11機能を使用する別の方法を次に示します。これはconstであり、STLコンテナーを継承せず、少し整理されています。

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair<int,std::string> mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; 
    std::vector<mapping> const nummap = 
    { 
        m(one,"one"), 
        m(two,"two"), 
        m(three,"three"),
        m(five,"five"),
        m(six,"six"),
        m(seven,"seven"),
    };
    for(auto i  : nummap)
    {
        if(i.first==static_cast<int>(e))
        {
            return i.second;
        }
    }
    return "";
}

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}
于 2009-06-23T01:45:40.893 に答える
4
#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
private:                                                            \
    static std::map<int, std::string> nameMap;                      \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static std::map<int, std::string> initMap()                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        map<int, string> tmp;                                       \
        for(vector<string>::iterator it = strings.begin();          \
                                               it != strings.end(); \
                                               ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
        return tmp;                                                 \
    }                                                               \
public:                                                             \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap[aInt];                                       \
    }                                                               \
};                                                                  \
std::map<int, std::string>                                          \
EnumName::nameMap = EnumName::initMap();

使用法:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
cout<<MyEnum::toString(MyEnum::TWO);
cout<<MyEnum::toString(10);
于 2014-02-27T11:34:59.310 に答える
4

Suma のマクロ ソリューションは素晴らしいです。ただし、2 つの異なるマクロを用意する必要はありません。C++ はヘッダーを 2 回インクルードします。インクルード ガードを除外するだけです。

したがって、 foobar.h を定義するだけです

ENUM(Foo, 1)
ENUM(Bar, 2)

次のように含めます。

#define ENUMFACTORY_ARGUMENT "foobar.h"
#include "enumfactory.h"

enumfactory.h は 2#include ENUMFACTORY_ARGUMENT秒かかります。最初のラウンドでは、Suma のように ENUM を展開しDECLARE_ENUMます。2 番目のラウンドでは、ENUM は のように機能しDEFINE_ENUMます。

ENUMFACTORY_ARGUMENT に異なる #define を渡す限り、enumfactory.h を複数回含めることもできます。

于 2008-10-14T16:03:03.873 に答える
3

変換関数は、理想的にはconst char * を返す必要があることに注意してください。

列挙型を個別のヘッダー ファイルに入れる余裕がある場合は、マクロを使用して次のようなことを行うことができます (ああ、これは醜いでしょう)。

#include "enum_def.h"
#include "colour.h"
#include "enum_conv.h"
#include "colour.h"

enum_def.h の場所:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) enum NAME {
#define ENUM_ADD(NAME, VALUE) NAME = VALUE,
#define ENUM_END };

そして enum_conv.h は次のとおりです。

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) {
#define ENUM_ADD(NAME, VALUE) case NAME: return #NAME;
#define ENUM_END default: return "Invalid value"; } }

そして最後に、 color.h には次のものがあります。

ENUM_START(colour)
ENUM_ADD(red,   0xff0000)
ENUM_ADD(green, 0x00ff00)
ENUM_ADD(blue,  0x0000ff)
ENUM_END

また、変換関数を次のように使用できます。

printf("%s", colour_to_string(colour::red));

これは見苦しいですが、(プリプロセッサ レベルで) コード内の 1 か所だけで列挙型を定義できる唯一の方法です。したがって、列挙型の変更によるコードのエラーが発生しにくくなります。列挙型の定義と変換関数は常に同期されます。ただし、繰り返しますが、これは醜いです:)

于 2008-10-14T16:05:31.170 に答える
3

これは、@ user3360260 の回答に対する変更です。以下の新機能があります

  • MyEnum fromString(const string&)サポート
  • VisualStudio 2012 でコンパイル
  • enum は (const 宣言だけでなく) 実際の POD 型であるため、変数に割り当てることができます。
  • C++ の「範囲」機能 (ベクトルの形式) を追加して、列挙型に対する「foreach」反復を許可します。

使用法:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
MyEnum foo = MyEnum::TWO;
cout << MyEnum::toString(foo);  // static method
cout << foo.toString();         // member method
cout << MyEnum::toString(MyEnum::TWO);
cout << MyEnum::toString(10);
MyEnum foo = myEnum::fromString("TWO");

// C++11 iteration over all values
for( auto x : MyEnum::allValues() )
{
  cout << x.toString() << endl;
}

これがコードです

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
public:                                                             \
    EnumName() : value(0) {}                                        \
    EnumName(int x) : value(x) {}                                   \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static void initMap(std::map<int, std::string>& tmp)                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        for(vector<string>::iterator it = strings.begin();          \
                                                it != strings.end(); \
                                                ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
    }                                                               \
    int value;                                                      \
public:                                                             \
    operator int () const { return value; }                         \
    std::string toString(void) const {                              \
            return toString(value);                                 \
    }                                                               \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap()[aInt];                                     \
    }                                                               \
    static EnumName fromString(const std::string& s)                \
    {                                                               \
        auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { \
            return p.second == s;                                   \
        });                                                         \
        if (it == nameMap().end()) {                                \
        /*value not found*/                                         \
            throw EnumName::Exception();                            \
        } else {                                                    \
            return EnumName(it->first);                             \
        }                                                           \
    }                                                               \
    class Exception : public std::exception {};                     \
    static std::map<int,std::string>& nameMap() {                   \
      static std::map<int,std::string> nameMap0;                    \
      if (nameMap0.size() ==0) initMap(nameMap0);                   \
      return nameMap0;                                              \
    }                                                               \
    static std::vector<EnumName> allValues() {                      \
      std::vector<EnumName> x{ __VA_ARGS__ };                       \
      return x;                                                     \
    }                                                               \
    bool operator<(const EnumName a) const { return (int)*this < (int)a; } \
};         

String への変換は高速な has ルックアップであるのに対し、String からの変換は低速な線形検索であることに注意してください。しかし、とにかく文字列 (および関連するファイル IO) は非常に高価であるため、bimap を最適化または使用する必要性を感じませんでした。

于 2014-05-01T20:17:52.427 に答える
3

別の答え: 状況によっては、CSV、YAML、または XML ファイルなどの非コード形式で列挙を定義し、その定義から C++ 列挙コードと to-string コードの両方を生成することが理にかなっています。このアプローチは、アプリケーションで実用的である場合とそうでない場合がありますが、覚えておく必要があります。

于 2008-10-14T17:45:10.173 に答える
3

ここに1ファイルのソリューションがあります(@Marcinによるエレガントな回答に基づく:

#include <iostream>

#define ENUM_TXT \
X(Red) \
X(Green) \
X(Blue) \
X(Cyan) \
X(Yellow) \
X(Magenta) \

enum Colours {
#   define X(a) a,
ENUM_TXT
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
ENUM_TXT
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c] << std::endl;
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}
于 2014-08-20T21:51:29.003 に答える
2

これは、マクロで生成される個別の列挙型ラッパークラスを使用して行います。いくつかの利点があります。

  • 定義していない列挙型に対してそれらを生成できます(例:OSプラットフォームヘッダー列挙型)
  • 範囲チェックをラッパークラスに組み込むことができます
  • ビットフィールド列挙型を使用して「よりスマートな」フォーマットを実行できます

もちろん、欠点は、フォーマッタクラスで列挙値を複製する必要があり、それらを生成するためのスクリプトがないことです。それ以外は、かなりうまくいくようです。

これが私のコードベースからの列挙型の例です。マクロとテンプレートを実装するすべてのフレームワークコードはありませんが、次のようなアイデアを得ることができます。

enum EHelpLocation
{
    HELP_LOCATION_UNKNOWN   = 0, 
    HELP_LOCAL_FILE         = 1, 
    HELP_HTML_ONLINE        = 2, 
};
class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation >
{
public:
    static inline CString FormatEnum( EHelpLocation eValue )
    {
        switch ( eValue )
        {
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE );
        default:
            return FormatAsNumber( eValue );
        }
    }
};
DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > );
typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation;

その場合、EHelpLocationを使用する代わりに、SEHelpLocationを使用するという考え方です。すべて同じように機能しますが、列挙型変数自体に対して範囲チェックと'Format()'メソッドを取得します。スタンドアロン値をフォーマットする必要がある場合は、CEnumFormatter_EHelpLocation :: FormatEnum(...)を使用できます。

これがお役に立てば幸いです。これは、実際に他のクラスを生成するためのスクリプトに関する元の質問にも対応していないことを理解していますが、構造が同じ問題を解決しようとする人、またはそのようなスクリプトを作成するのに役立つことを願っています。

于 2008-10-14T19:40:13.717 に答える
2

これはBOOSTを使用した私のソリューションでした:

#include <boost/preprocessor.hpp>

#define X_STR_ENUM_TOSTRING_CASE(r, data, elem)                                 \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define X_ENUM_STR_TOENUM_IF(r, data, elem)                                     \
    else if(data == BOOST_PP_STRINGIZE(elem)) return elem;

#define STR_ENUM(name, enumerators)                                             \
    enum name {                                                                 \
        BOOST_PP_SEQ_ENUM(enumerators)                                          \
    };                                                                          \
                                                                                \
    inline const QString enumToStr(name v)                                      \
    {                                                                           \
        switch (v)                                                              \
        {                                                                       \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                X_STR_ENUM_TOSTRING_CASE,                                       \
                name,                                                           \
                enumerators                                                     \
            )                                                                   \
                                                                                \
            default:                                                            \
                return "[Unknown " BOOST_PP_STRINGIZE(name) "]";                \
        }                                                                       \
    }                                                                           \
                                                                                \
    template <typename T>                                                       \
    inline const T strToEnum(QString v);                                        \
                                                                                \
    template <>                                                                 \
    inline const name strToEnum(QString v)                                      \
    {                                                                           \
        if(v=="")                                                               \
            throw std::runtime_error("Empty enum value");                       \
                                                                                \
        BOOST_PP_SEQ_FOR_EACH(                                                  \
            X_ENUM_STR_TOENUM_IF,                                               \
            v,                                                                  \
            enumerators                                                         \
        )                                                                       \
                                                                                \
        else                                                                    \
            throw std::runtime_error(                                           \
                        QString("[Unknown value %1 for enum %2]")               \
                            .arg(v)                                             \
                            .arg(BOOST_PP_STRINGIZE(name))                      \
                                .toStdString().c_str());                        \
    }

列挙型を作成するには、次のように宣言します。

STR_ENUM
(
    SERVICE_RELOAD,
        (reload_log)
        (reload_settings)
        (reload_qxml_server)
)

変換の場合:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log");
QString serviceReloadStr = enumToStr(reload_log);
于 2014-08-28T17:40:23.543 に答える
2

これは未リリースのソフトウェアですが、Frank Laub の BOOST_ENUM が適しているようです。私が気に入っている部分は、クラスのスコープ内で列挙型を定義できることです。これは、ほとんどのマクロベースの列挙型では通常許可されていません。Boost Vault にあり ます: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=&新しい Boost リリースでどれだけうまくコンパイルできるかを知っています。使用例については、libs/test を参照してください。

于 2009-11-04T16:22:03.577 に答える
2

誰かが役に立つと思った場合に備えて、これを投稿したいと思います。

私の場合、1つのファイルから 1 つの C++11 列挙型を生成ToString()して関数を作成するだけです。FromString().hpp

列挙項目を含むヘッダー ファイルを解析し、新しい.cppファイルに関数を生成する Python スクリプトを作成しました。

このスクリプトは、execute_processを使用して CMakeLists.txt に追加するか、Visual Studio のビルド前イベントとして追加できます。ファイルは.cpp自動的に生成されるため、新しい列挙項目が追加されるたびに手動で更新する必要はありません。

generate_enum_strings.py

# This script is used to generate strings from C++ enums

import re
import sys
import os

fileName = sys.argv[1]
enumName = os.path.basename(os.path.splitext(fileName)[0])

with open(fileName, 'r') as f:
    content = f.read().replace('\n', '')

searchResult = re.search('enum(.*)\{(.*?)\};', content)
tokens = searchResult.group(2)
tokens = tokens.split(',')
tokens = map(str.strip, tokens)
tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens)

textOut = ''
textOut += '\n#include "' + enumName + '.hpp"\n\n'
textOut += 'namespace myns\n'
textOut += '{\n'
textOut += '    std::string ToString(ErrorCode errorCode)\n'
textOut += '    {\n'
textOut += '        switch (errorCode)\n'
textOut += '        {\n'

for token in tokens:
    textOut += '        case ' + enumName + '::' + token + ':\n'
    textOut += '            return "' + token + '";\n'

textOut += '        default:\n'
textOut += '            return "Last";\n'
textOut += '        }\n'
textOut += '    }\n'
textOut += '\n'
textOut += '    ' + enumName + ' FromString(const std::string &errorCode)\n'
textOut += '    {\n'
textOut += '        if ("' + tokens[0] + '" == errorCode)\n'
textOut += '        {\n'
textOut += '            return ' + enumName + '::' + tokens[0] + ';\n'
textOut += '        }\n'

for token in tokens[1:]:
    textOut += '        else if("' + token + '" == errorCode)\n'
    textOut += '        {\n'
    textOut += '            return ' + enumName + '::' + token + ';\n'
    textOut += '        }\n'

textOut += '\n'
textOut += '        return ' + enumName + '::Last;\n'
textOut += '    }\n'
textOut += '}\n'

fileOut = open(enumName + '.cpp', 'w')
fileOut.write(textOut)

例:

エラーコード.hpp

#pragma once

#include <string>
#include <cstdint>

namespace myns
{
    enum class ErrorCode : uint32_t
    {
        OK = 0,
        OutOfSpace,
        ConnectionFailure,
        InvalidJson,
        DatabaseFailure,
        HttpError,
        FileSystemError,
        FailedToEncrypt,
        FailedToDecrypt,
        EndOfFile,
        FailedToOpenFileForRead,
        FailedToOpenFileForWrite,
        FailedToLaunchProcess,

        Last
    };

    std::string ToString(ErrorCode errorCode);
    ErrorCode FromString(const std::string &errorCode);
}

走るpython generate_enum_strings.py ErrorCode.hpp

結果:

エラーコード.cpp

#include "ErrorCode.hpp"

namespace myns
{
    std::string ToString(ErrorCode errorCode)
    {
        switch (errorCode)
        {
        case ErrorCode::OK:
            return "OK";
        case ErrorCode::OutOfSpace:
            return "OutOfSpace";
        case ErrorCode::ConnectionFailure:
            return "ConnectionFailure";
        case ErrorCode::InvalidJson:
            return "InvalidJson";
        case ErrorCode::DatabaseFailure:
            return "DatabaseFailure";
        case ErrorCode::HttpError:
            return "HttpError";
        case ErrorCode::FileSystemError:
            return "FileSystemError";
        case ErrorCode::FailedToEncrypt:
            return "FailedToEncrypt";
        case ErrorCode::FailedToDecrypt:
            return "FailedToDecrypt";
        case ErrorCode::EndOfFile:
            return "EndOfFile";
        case ErrorCode::FailedToOpenFileForRead:
            return "FailedToOpenFileForRead";
        case ErrorCode::FailedToOpenFileForWrite:
            return "FailedToOpenFileForWrite";
        case ErrorCode::FailedToLaunchProcess:
            return "FailedToLaunchProcess";
        case ErrorCode::Last:
            return "Last";
        default:
            return "Last";
        }
    }

    ErrorCode FromString(const std::string &errorCode)
    {
        if ("OK" == errorCode)
        {
            return ErrorCode::OK;
        }
        else if("OutOfSpace" == errorCode)
        {
            return ErrorCode::OutOfSpace;
        }
        else if("ConnectionFailure" == errorCode)
        {
            return ErrorCode::ConnectionFailure;
        }
        else if("InvalidJson" == errorCode)
        {
            return ErrorCode::InvalidJson;
        }
        else if("DatabaseFailure" == errorCode)
        {
            return ErrorCode::DatabaseFailure;
        }
        else if("HttpError" == errorCode)
        {
            return ErrorCode::HttpError;
        }
        else if("FileSystemError" == errorCode)
        {
            return ErrorCode::FileSystemError;
        }
        else if("FailedToEncrypt" == errorCode)
        {
            return ErrorCode::FailedToEncrypt;
        }
        else if("FailedToDecrypt" == errorCode)
        {
            return ErrorCode::FailedToDecrypt;
        }
        else if("EndOfFile" == errorCode)
        {
            return ErrorCode::EndOfFile;
        }
        else if("FailedToOpenFileForRead" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForRead;
        }
        else if("FailedToOpenFileForWrite" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForWrite;
        }
        else if("FailedToLaunchProcess" == errorCode)
        {
            return ErrorCode::FailedToLaunchProcess;
        }
        else if("Last" == errorCode)
        {
            return ErrorCode::Last;
        }

        return ErrorCode::Last;
    }
}
于 2016-11-01T23:40:37.407 に答える
1

次の ruby​​ スクリプトは、ヘッダーの解析を試み、元のヘッダーと共に必要なソースをビルドします。

#! /usr/bin/env ruby

# Let's "parse" the headers
# Note that using a regular expression is rather fragile
# and may break on some inputs

GLOBS = [
  "toto/*.h",
  "tutu/*.h",
  "tutu/*.hxx"
]

enums = {}
GLOBS.each { |glob|
  Dir[glob].each { |header|
    enums[header] = File.open(header, 'rb') { |f|
      f.read
    }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values|
      [
        enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value|
          enum_key_and_value.split(/\s*=\s*/).first
        }
      ]
    }
  }
}


# Now we build a .h and .cpp alongside the parsed headers
# using the template engine provided with ruby
require 'erb'

template_h = ERB.new <<-EOS
#ifndef <%= enum_name %>_to_string_h_
#define <%= enum_name %>_to_string_h_ 1

#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);

#endif
EOS

template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"

char* enum_to_string(<%= enum_name %> e)
{
  switch (e)
  {<% enum_keys.each do |enum_key| %>
    case <%= enum_key %>: return "<%= enum_key %>";<% end %>
    default: return "INVALID <%= enum_name %> VALUE";
  }
}
EOS

enums.each { |header, enum_name_and_keys|
  enum_name_and_keys.each { |enum_name, enum_keys|
    File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h|
      built_h.write(template_h.result(binding))
    }

    File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
      built_cpp.write(template_cpp.result(binding))
    }
  }
}

正規表現を使用すると、この「パーサー」は非常に壊れやすくなり、特定のヘッダーを適切に処理できない場合があります。

列挙型 MyEnum および MyEnum2 の定義を含むヘッダー toto/ah があるとします。スクリプトは以下をビルドします。

toto/MyEnum_to_string.h
toto/MyEnum_to_string.cpp
toto/MyEnum2_to_string.h
toto/MyEnum2_to_string.cpp

より堅牢なソリューションは次のとおりです。

  • 列挙型とその操作を定義するすべてのソースを別のソースから構築します。これは、C/C++ よりもはるかに解析しやすい XML/YML/その他のファイルで列挙型を定義することを意味します。
  • Avdi が提案するような実際のコンパイラを使用してください。
  • テンプレートの有無にかかわらず、プリプロセッサ マクロを使用します。
于 2008-10-14T17:10:31.817 に答える
1

答え 0 の問題は、enum のバイナリ値が必ずしも 0 から始まるとは限らず、必ずしも連続しているとは限らないことです。

これが必要なときは、通常、次のようにします。

  • 列挙型の定義をソースにプルする
  • 名前だけを取得するように編集します
  • マクロを実行して、名前を質問の case 句に変更しますが、通常は 1 行で行います。 case foo: return "foo";
  • スイッチ、デフォルト、およびその他の構文を追加して、合法的にします
于 2008-10-14T15:50:22.057 に答える
0

これは、列挙型を文字列に簡単に変換するために私が書いた CLI プログラムです。使い方は簡単で、完了までに約 5 秒かかります (プログラムを含むディレクトリに cd してから実行し、enum を含むファイルを渡す時間を含みます)。

ここからダウンロード: http://www.mediafire.com/?nttignoozzz

ここでの議論のトピック: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

「--help」引数を指定してプログラムを実行すると、使用方法の説明が表示されます。

于 2010-06-07T22:40:49.423 に答える
0

少し前に、列挙型を QComboBox に適切に表示し、列挙型と文字列表現を 1 つのステートメントとして定義するためのトリックを作成しました。

#pragma once
#include <boost/unordered_map.hpp>

namespace enumeration
{

   struct enumerator_base : boost::noncopyable
   {
      typedef
         boost::unordered_map<int, std::wstring>
         kv_storage_t;
      typedef
         kv_storage_t::value_type
         kv_type;
      kv_storage_t const & kv() const
      {
         return storage_;
      }

      LPCWSTR name(int i) const
      {
         kv_storage_t::const_iterator it = storage_.find(i);
         if(it != storage_.end())
            return it->second.c_str();
         return L"empty";
      }

   protected:
      kv_storage_t storage_;
   };

   template<class T>
   struct enumerator;

   template<class D>
   struct enum_singleton : enumerator_base
   {
      static enumerator_base const & instance()
      {
         static D inst;
         return inst;
      }
   };
}

#define QENUM_ENTRY(K, V, N)  K, N storage_.insert(std::make_pair((int)K, V));

#define QBEGIN_ENUM(NAME, C)   \
enum NAME                     \
{                             \
   C                          \
}                             \
};                            \
}                             \

#define QEND_ENUM(NAME) \
};                     \
namespace enumeration  \
{                      \
template<>             \
struct enumerator<NAME>\
   : enum_singleton< enumerator<NAME> >\
{                      \
   enumerator()        \
   {

//usage
/*
QBEGIN_ENUM(test_t,
   QENUM_ENTRY(test_entry_1, L"number uno",
   QENUM_ENTRY(test_entry_2, L"number dos",
   QENUM_ENTRY(test_entry_3, L"number tres",
QEND_ENUM(test_t)))))
*/

これenumeration::enum_singleton<your_enum>::instance()で、列挙型を文字列に変換できるようになりました。に置き換えるkv_storage_tboost::bimap、逆変換もできるようになります。Qtオブジェクトをテンプレートにすることができなかったため、コンバーターの共通基本クラスが導入され、Qtオブジェクトに保存されました

以前の出演

于 2013-03-07T11:47:49.453 に答える
0

それができる唯一の方法です(文字列の配列も機能します)。

問題は、C プログラムがコンパイルされると、列挙型のバイナリ値だけが使用され、名前がなくなることです。

于 2008-10-14T15:17:27.117 に答える
0

C ++で列挙の「単語」を出力するという自分の問題の解決策を探していたときに、この質問に出くわしました。提示された質問に言葉どおりに答える簡単な解決策を提供するために戻ってきました。必要なのは、enum リストをベクトルで「ミラーリング」することだけです。

enum class genre { Fiction, NonFiction, Periodical, Biography, Children };

vector<string>genre_tbl { "Fiction", "NonFiction", "Periodical", "Biography", "Children" };

上記で入力した列挙型は、デフォルトで次のことを行うためです。

Fiction = 0
NonFiction = 1
Periodical = 2
Biography = 3
Children = 4

これは、列挙型から文字列への変換を非常に簡単にするベクトル位置に一致します。

string s1 = genre_tbl[int(genre::fiction)];

私の問題では、ジャンル タイプの Gen というメンバーを持つ Book というユーザー定義クラスを作成しました。プログラムは、ジャンルを単語として出力できる必要がありました。

class book {...};
ostream& operator<<(ostream& os, genre g) { return os << genre_tbl[int(g)]; }

book b1;
b1.Gen = genre(0)
cout << b1.Gen;

この場合、どの「フィクション」が画面に出力されるか。

于 2018-07-09T23:00:54.013 に答える