94

これが私がやろうとしていることです:

typedef enum { ONE, TWO, THREE } Numbers;

次のようなスイッチケースを実行する関数を作成しようとしています:

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

すべてのケースで定義する代わりに、上記のように enum 変数を使用して設定する方法はありますか?

4

20 に答える 20

72

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マクロとの比較

これは他の人が言及したX-Macrosに似ていますが、このソリューションは#undefingを必要としないという点でよりエレガントだと思います。これにより、工場でヘッダーファイル(ヘッダーファイル)にある複雑なものをより多く隠すことができます。これは、新しい列挙型を定義する必要があるときにまったく触れていないものです。したがって、新しい列挙型の定義ははるかに短く、よりクリーンです。

于 2008-10-14T19:33:50.157 に答える
61
// 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
于 2008-09-29T05:00:33.757 に答える
16

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

于 2008-09-29T02:06:34.533 に答える
13

これを行う方法は間違いなくあります - X() マクロを使用します。これらのマクロは、C プリプロセッサを使用して、ソース データのリストから列挙型、配列、およびコード ブロックを構築します。X() マクロを含む #define に新しい項目を追加するだけです。switch ステートメントは自動的に展開されます。

あなたの例は次のように書くことができます:

 // 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 マクロを使用して文字列配列と列挙型インデックスを作成する方法) はありますが、これは最も単純なデモです。

于 2008-09-29T13:17:09.727 に答える
8

確かな答えがいくつかあることは知っていますが、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]);
}
于 2008-09-29T13:48:46.390 に答える
7

接吻。列挙型を使用して他のあらゆる種類のスイッチ/ケースを行うことになりますが、なぜ印刷が異なる必要があるのでしょうか? 大文字小文字の区別を忘れることができる場所が他に約 100 あることを考えると、印刷ルーチンで大文字小文字を忘れることは大したことではありません。-Wall をコンパイルするだけで、網羅的ではない大文字と小文字の一致を警告します。「デフォルト」を使用しないでください。これにより、スイッチが完全になり、警告が表示されなくなります。代わりに、スイッチを終了させ、デフォルトのケースを次のように処理します...

const char *myenum_str(myenum e)
{
    switch(e) {
    case ONE: return "one";
    case TWO: return "two";
    }
    return "invalid";
}
于 2010-07-05T15:46:16.883 に答える
6

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;

そこには大きなギャップがあるからです。

この方法の利点は、列挙型と文字列の定義を互いに近づけることです。関数内に switch ステートメントがあると、それらが分離されます。これは、一方を他方なしで変更する可能性が低いことを意味します。

于 2008-09-29T02:44:23.257 に答える
4

boost::preprocessorを使用すると、次のような洗練されたソリューションが可能になります。

ステップ 1: ヘッダー ファイルをインクルードします。

#include "EnumUtilities.h"

ステップ 2: 次の構文で列挙型オブジェクトを宣言します。

MakeEnum( TestData,
         (x)
         (y)
         (z)
         );

ステップ 3: データを使用する:

要素数の取得:

td::cout << "Number of Elements: " << TestDataCount << std::endl;

関連する文字列を取得する:

std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;

関連付けられた文字列から列挙値を取得します。

std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
std::cout << "Value of z is " << TestData2Enum("z") << std::endl;

これはクリーンでコンパクトに見え、含める余分なファイルはありません。EnumUtilities.h 内に記述したコードは次のとおりです。

#include <boost/preprocessor/seq/for_each.hpp>
#include <string>

#define REALLY_MAKE_STRING(x) #x
#define MAKE_STRING(x) REALLY_MAKE_STRING(x)
#define MACRO1(r, data, elem) elem,
#define MACRO1_STRING(r, data, elem)    case elem: return REALLY_MAKE_STRING(elem);
#define MACRO1_ENUM(r, data, elem)      if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;


#define MakeEnum(eName, SEQ) \
    enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
    last_##eName##_enum}; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    }; \
    static enum eName eName##2Enum(const std::string eStrEl) \
    { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
        return (enum eName)0; \
    };

いくつかの制限があります。つまり、boost::preprocessor のものです。この場合、定数のリストは 64 要素を超えることはできません。

同じロジックに従って、疎な列挙型を作成することも考えられます。

#define EnumName(Tuple)                 BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
#define EnumValue(Tuple)                BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
#define MACRO2(r, data, elem)           EnumName(elem) EnumValue(elem),
#define MACRO2_STRING(r, data, elem)    case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));

#define MakeEnumEx(eName, SEQ) \
    enum eName { \
    BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
    last_##eName##_enum }; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    };  

この場合、構文は次のとおりです。

MakeEnumEx(TestEnum,
           ((x,))
           ((y,=1000))
           ((z,))
           );

使用法は上記と同様です (前の構文から推測できる eName##2Enum 関数を除いて)。

Mac と Linux でテストしましたが、boost::preprocessor は完全には移植できない可能性があることに注意してください。

于 2012-02-23T02:02:03.277 に答える
4

Converting C++ enums to strings を試してください。コメントには、列挙項目が任意の値を持つ場合の問題を解決する改善があります。

于 2008-09-29T02:28:01.440 に答える
2

gcc を使用している場合は、次を使用できます。

const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};

次に、たとえば呼び出します

enum_to_string_map[enum1]
于 2014-04-28T13:40:01.780 に答える
1

Mu Dynamics Research Labs - Blog Archiveでアイデアをチェックしてください。私は今年初めにこれを見つけました - 私はそれを見つけた正確な文脈を忘れてしまいました - そしてそれをこのコードに適応させました. 先頭に E を追加することのメリットについて議論することができます。それは対処された特定の問題に適用できますが、一般的な解決策の一部ではありません。これを「vignettes」フォルダーに隠しておきました。ここには、後で必要になった場合に備えて、興味深いコードのスクラップを保管しています。このアイデアがどこから来たのか、その時点でメモをとっていなかったと言うのは恥ずかしいことです.

ヘッダー: 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 プリプロセッサの世界で最もクリーンな使用方法ではありませんが、マテリアルを何度も書き出すことはできません。

于 2008-09-29T04:19:11.097 に答える
1

何かを C 識別子と文字列の両方にする

于 2008-09-29T09:29:33.173 に答える
0

次の機能を持つマクロを使用したソリューションを次に示します。

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

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

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

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

https://stackoverflow.com/a/20134475/1812866

于 2013-11-22T00:45:18.043 に答える
0
#define stringify( name ) # name

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

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

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

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

于 2012-11-02T02:46:31.890 に答える
0

streamable_enumストリーム演算子を使用<<する単純なテンプレート クラスを作成し、以下>>に基づいていstd::map<Enum, std::string>ます。

#ifndef STREAMABLE_ENUM_HPP
#define STREAMABLE_ENUM_HPP

#include <iostream>
#include <string>
#include <map>

template <typename E>
class streamable_enum
{
public:
    typedef typename std::map<E, std::string> tostr_map_t;
    typedef typename std::map<std::string, E> fromstr_map_t;

    streamable_enum()
    {}

    streamable_enum(E val) :
        Val_(val)
    {}

    operator E() {
        return Val_;
    }

    bool operator==(const streamable_enum<E>& e) {
        return this->Val_ == e.Val_;
    }

    bool operator==(const E& e) {
        return this->Val_ == e;
    }

    static const tostr_map_t& to_string_map() {
        static tostr_map_t to_str_(get_enum_strings<E>());
        return to_str_;
    }

    static const fromstr_map_t& from_string_map() {
        static fromstr_map_t from_str_(reverse_map(to_string_map()));
        return from_str_;
    }
private:
    E Val_;

    static fromstr_map_t reverse_map(const tostr_map_t& eToS) {
        fromstr_map_t sToE;
        for (auto pr : eToS) {
            sToE.emplace(pr.second, pr.first);
        }
        return sToE;
    }
};

template <typename E>
streamable_enum<E> stream_enum(E e) {
    return streamable_enum<E>(e);
}

template <typename E>
typename streamable_enum<E>::tostr_map_t get_enum_strings() {
    // \todo throw an appropriate exception or display compile error/warning
    return {};
}

template <typename E>
std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
    auto& mp = streamable_enum<E>::to_string_map();
    auto res = mp.find(e);
    if (res != mp.end()) {
        os << res->second;
    } else {
        os.setstate(std::ios_base::failbit);
    }
    return os;
}

template <typename E>
std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
    std::string str;
    is >> str;
    if (str.empty()) {
        is.setstate(std::ios_base::failbit);
    }
    auto& mp = streamable_enum<E>::from_string_map();
    auto res = mp.find(str);
    if (res != mp.end()) {
        e = res->second;
    } else {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

#endif

使用法:

#include "streamable_enum.hpp"

using std::cout;
using std::cin;
using std::endl;

enum Animal {
    CAT,
    DOG,
    TIGER,
    RABBIT
};

template <>
streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
    return {
        { CAT, "Cat"},
        { DOG, "Dog" },
        { TIGER, "Tiger" },
        { RABBIT, "Rabbit" }
    };
}

int main(int argc, char* argv []) {
    cout << "What animal do you want to buy? Our offering:" << endl;
    for (auto pr : streamable_enum<Animal>::to_string_map()) {          // Use from_string_map() and pr.first instead
        cout << " " << pr.second << endl;                               // to have them sorted in alphabetical order
    }
    streamable_enum<Animal> anim;
    cin >> anim;
    if (!cin) {
        cout << "We don't have such animal here." << endl;
    } else if (anim == Animal::TIGER) {
        cout << stream_enum(Animal::TIGER) << " was a joke..." << endl;
    } else {
        cout << "Here you are!" << endl;
    }

    return 0;
}
于 2013-07-31T23:11:47.437 に答える
0

列挙インデックスが 0 ベースの場合、名前を char* の配列に入れ、それらに列挙値でインデックスを付けることができます。

于 2008-09-29T02:06:39.433 に答える
0

構造体とクラスを適応させる Boost.Fusion のようなソリューションがいいと思いました。ある時点で、列挙型を融合シーケンスとして使用することさえありました。

そこで、列挙型を出力するコードを生成するための小さなマクロをいくつか作成しました。これは完全ではなく、Boost.Fusion で生成されたボイラープレート コードとは関係ありませんが、Boost Fusion マクロのように使用できます。Boost.Fusion が構造体メンバーの名前を出力できるようにするこのインフラストラクチャに統合するために必要な型を実際に生成したいのですが、これは後で行われます。今のところ、これは単なるマクロです。

#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP

#include <swissarmyknife/detail/config.hpp>

#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>


#define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C(                     \
    R, unused, ENUMERATION_ENTRY)                                               \
    case ENUMERATION_ENTRY:                                                     \
      return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY);                             \
    break;                                                                      

/**
 * \brief Adapts ENUM to reflectable types.
 *
 * \param ENUM_TYPE To be adapted
 * \param ENUMERATION_SEQ Sequence of enum states
 */
#define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ)                   \
    inline std::string to_string(const ENUM_TYPE& enum_value) {                 \
      switch (enum_value) {                                                     \
      BOOST_PP_SEQ_FOR_EACH(                                                    \
          SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C,                   \
          unused, ENUMERATION_SEQ)                                              \
        default:                                                                \
          return BOOST_PP_STRINGIZE(ENUM_TYPE);                                 \
      }                                                                         \
    }                                                                           \
                                                                                \
    inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
      os << to_string(value);                                                   \
      return os;                                                                \
    }

#endif

以下の古い回答はかなり悪いので、使用しないでください。:)

古い答え:

enums 宣言構文をあまり変更せずにこの問題を解決する方法を探していました。プリプロセッサを使用して、文字列化された列挙宣言から文字列を取得するソリューションにたどり着きました。

次のような非スパース列挙型を定義できます。

SMART_ENUM(State, 
    enum State {
        RUNNING,
        SLEEPING, 
        FAULT, 
        UNKNOWN
    })

そして、さまざまな方法でそれらとやり取りできます。

// With a stringstream
std::stringstream ss;
ss << State::FAULT;
std::string myEnumStr = ss.str();

//Directly to stdout
std::cout << State::FAULT << std::endl;

//to a string
std::string myStr = State::to_string(State::FAULT);

//from a string
State::State myEnumVal = State::from_string(State::FAULT);

以下の定義に基づく:

#define SMART_ENUM(enumTypeArg, ...)                                                     \
namespace enumTypeArg {                                                                  \
    __VA_ARGS__;                                                                         \
    std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) {                 \
            os << swissarmyknife::enums::to_string(#__VA_ARGS__, val);                   \
            return os;                                                                   \
    }                                                                                    \
                                                                                     \
    std::string to_string(const enumTypeArg& val) {                                      \
            return swissarmyknife::enums::to_string(#__VA_ARGS__, val);                  \
    }                                                                                    \
                                                                                     \
    enumTypeArg from_string(const std::string &str) {                                    \
            return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str);   \
    }                                                                                    \
}                                                                                        \


namespace swissarmyknife { namespace enums {

    static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            if (enumVal == count) {
                std::string identifiersSubset = identifiers.substr(0, found);
                size_t beginId = identifiersSubset.find_last_of("{,");
                identifiersSubset = identifiersSubset.substr(beginId+1);
                boost::algorithm::trim(identifiersSubset);
                return identifiersSubset;
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("The enum declaration provided doesn't contains this state.");
    }                                                  

    template <typename EnumType>
    static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            std::string identifiersSubset = identifiers.substr(0, found);
            size_t beginId = identifiersSubset.find_last_of("{,");
            identifiersSubset = identifiersSubset.substr(beginId+1);
            boost::algorithm::trim(identifiersSubset);

            if (identifiersSubset == enumStr) {
                return static_cast<EnumType>(count);
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("No valid enum value for the provided string");
    }                      

}}

sparse enum のサポートが必要になり、さらに時間があれば、boost::xpressive を使用してto_stringおよびfrom_stringの実装を改善しますが、実行される重要なテンプレートと生成される実行可能ファイルが原因で、コンパイル時間がかかります。本当に大きくなる可能性があります。しかし、これには、この見苦しい手動の文字列操作コードよりも読みやすく、保守しやすいという利点があります。:D

それ以外の場合は、常にboost::bimapを使用して列挙型の値と文字列の間でこのようなマッピングを実行しましたが、手動で維持する必要があります.

于 2012-09-19T17:13:22.283 に答える