38

あなたが尋ねる前に、私はSOでこれを探し探しましたが、確かな答えを見つけることができません.

例として、非増分値を持つ列挙型を動的に反復できる必要があります。

typedef enum {
    CAPI_SUBTYPE_NULL = 0,               /* Null subtype. */
    CAPI_SUBTYPE_DIAG_DFD = 1,           /* Data Flow diag. */
    CAPI_SUBTYPE_DIAG_ERD = 2,           /* Entity-Relationship diag. */
    CAPI_SUBTYPE_DIAG_STD = 3,           /* State Transition diag. */
    CAPI_SUBTYPE_DIAG_STC = 4,           /* Structure Chart diag. */
    CAPI_SUBTYPE_DIAG_DSD = 5,           /* Data Structure diag. */
    CAPI_SUBTYPE_SPEC_PROCESS = 6,       /* Process spec. */
    CAPI_SUBTYPE_SPEC_MODULE = 7,        /* Module spec. */
    CAPI_SUBTYPE_SPEC_TERMINATOR = 8,    /* Terminator spec. */

    CAPI_SUBTYPE_DD_ALL = 13,            /* DD Entries (All). */
    CAPI_SUBTYPE_DD_COUPLE = 14,         /* DD Entries (Couples). */
    CAPI_SUBTYPE_DD_DATA_AREA = 15,      /* DD Entries (Data Areas). */
    CAPI_SUBTYPE_DD_DATA_OBJECT = 16,    /* DD Entries (Data Objects). */
    CAPI_SUBTYPE_DD_FLOW = 17,           /* DD Entries (Flows). */
    CAPI_SUBTYPE_DD_RELATIONSHIP = 18,   /* DD Entries (Relationships). */
    CAPI_SUBTYPE_DD_STORE = 19,          /* DD Entries (Stores). */

    CAPI_SUBTYPE_DIAG_PAD = 35,          /* Physical architecture diagram. */
    CAPI_SUBTYPE_DIAG_BD  = 36,          /* Behaviour diagram. */
    CAPI_SUBTYPE_DIAG_UCD = 37,          /* UML Use case diagram. */
    CAPI_SUBTYPE_DIAG_PD  = 38,          /* UML Package diagram. */
    CAPI_SUBTYPE_DIAG_COD = 39,          /* UML Collaboration diagram. */
    CAPI_SUBTYPE_DIAG_SQD = 40,          /* UML Sequence diagram. */
    CAPI_SUBTYPE_DIAG_CD  = 41,          /* UML Class diagram. */
    CAPI_SUBTYPE_DIAG_SCD = 42,          /* UML State chart. */
    CAPI_SUBTYPE_DIAG_ACD = 43,          /* UML Activity chart. */
    CAPI_SUBTYPE_DIAG_CPD = 44,          /* UML Component diagram. */
    CAPI_SUBTYPE_DIAG_DPD = 45,          /* UML Deployment diagram. */
    CAPI_SUBTYPE_DIAG_PFD = 47,          /* Process flow diagram. */
    CAPI_SUBTYPE_DIAG_HIER = 48,         /* Hierarchy diagram. */
    CAPI_SUBTYPE_DIAG_IDEF0 = 49,        /* IDEF0 diagram. */
    CAPI_SUBTYPE_DIAG_AID = 50,          /* AID diagram. */
    CAPI_SUBTYPE_DIAG_SAD = 51,          /* SAD diagram. */
    CAPI_SUBTYPE_DIAG_ASG = 59           /* ASG diagram. */
} CAPI_SUBTYPE_E ;

私がこれを可能にしたい理由は、列挙型が API で与えられ (明らかに変更することはできません)、API のバージョンに関係なく、これらの値を反復処理できるようにしたいからです。 .

どんな方向でも大歓迎です。

4

15 に答える 15

17

C++ では、列挙型を反復処理する唯一の方法は、それらを配列に格納し、同じものを反復処理することです。enum主な課題は、宣言と配列宣言で同じ順序を追跡する方法です。配列と同様にそれらを並べ替える方法を自動化
できます。これはまともな方法だと思います:enum

// CAPI_SUBTYPE_E_list.h
// This header file contains all the enum in the order
// Whatever order is set will be followed everywhere
NAME_VALUE(CAPI_SUBTYPE_NULL, 0),         /* Null subtype. */
NAME_VALUE(CAPI_SUBTYPE_DIAG_DFD, 1),     /* Data Flow diag. */
NAME_VALUE(CAPI_SUBTYPE_DIAG_ERD, 2),     /* Entity-Relationship diag. */
...
NAME_VALUE(CAPI_SUBTYPE_DD_ALL, 13),      /* DD Entries (All). */
NAME_VALUE(CAPI_SUBTYPE_DD_COUPLE, 14),   /* DD Entries (Couples). */
...
NAME_VALUE(CAPI_SUBTYPE_DIAG_ASG, 59)     /* ASG diagram. */

#includeこれで、enum 宣言と配列宣言でこのファイルをマクロの再定義で両方の場所に配置できます。

// Enum.h
typedef enum {
#define NAME_VALUE(NAME,VALUE) NAME = VALUE
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
}CAPI_SUBTYPE_E;

そして、他のマクロ定義を使用して配列に同じファイルを配置します。

// array file
// Either this array can be declared `static` or inside unnamed `namespace` to make 
// ... it visible through a header file; Or it should be declared `extern` and keep ...
// ...  the record of its size; declare a getter method for both array and the size
unsigned int CAPI_SUBTYPE_E_Array [] = {
#define NAME_VALUE(NAME,VALUE) NAME
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
};

C++03 で次のように繰り返します。

for(unsigned int i = 0, size = sizeof(CAPI_SUBTYPE_E_Array)/sizeof(CAPI_SUBTYPE_E_Array[0]);
    i < size; ++i)

またはC ++ 11ではまだ単純です:

for(auto i : CAPI_SUBTYPE_E_Array)
于 2013-05-19T02:52:24.650 に答える
12

これはトリッキーで、C++ の練習よりも C の方が多いですが、X マクロを使用できます。これは非常に見苦しく、TABLE を正しい順序に保つ必要があります。C++ では、列挙を繰り返す必要はなく、列挙に値を代入する必要もないと思います (表面上、列挙値はすべてのコンパイルでランダムです)。だから冗談だと思ってください:)

#include <iostream>

#define CAPI_SUBTYPE_TABLE \
    CAPI_SUBTYPE_X(CAPI_SUBTYPE_NULL,     0 ) \
    CAPI_SUBTYPE_X(CAPI_SUBTYPE_DIAG_DFD, 1 ) \
    CAPI_SUBTYPE_X(CAPI_SUBTYPE_DD_ALL,   13)

#define CAPI_SUBTYPE_X(name, value) name = value,
enum CAPI_SUBTYPE
{
    CAPI_SUBTYPE_TABLE
    CAPI_SUBTYPE_END
};
#undef CAPI_SUBTYPE_X

#define CAPI_SUBTYPE_X(name, value) name,
CAPI_SUBTYPE subtype_iteratable[] =
{
    CAPI_SUBTYPE_TABLE
    CAPI_SUBTYPE_END
};
#undef CAPI_SUBTYPE_X

#define CAPI_SUBTYPE_SIZE  (sizeof(subtype_iteratable) / sizeof(subtype_iteratable[0]) - 1)


int main()
{
    for (unsigned i = 0; i < CAPI_SUBTYPE_SIZE; ++i)
        std::cout << subtype_iteratable[i] << std::endl; // 0, 1, 13
}
于 2013-05-16T20:43:49.523 に答える
3

質問の冒頭に記載されている記事に基づいて、病人の範囲を知っているという前提に基づいた解決策を導き出しました。

これが良い解決策であるかどうかを本当に知りたいです。

まず、次のようなもので enum を終了しますCAPI_END = 60。交流するのに役立ちます。だから私のコードは次のとおりです。

typedef enum {
    CAPI_SUBTYPE_NULL = 0,               /* Null subtype. */
    CAPI_SUBTYPE_DIAG_DFD = 1,           /* Data Flow diag. */
    CAPI_SUBTYPE_DIAG_ERD = 2,           /* Entity-Relationship diag. */
    CAPI_SUBTYPE_DIAG_STD = 3,           /* State Transition diag. */
    CAPI_SUBTYPE_DIAG_STC = 4,           /* Structure Chart diag. */
    CAPI_SUBTYPE_DIAG_DSD = 5,           /* Data Structure diag. */
    CAPI_SUBTYPE_SPEC_PROCESS = 6,       /* Process spec. */
    CAPI_SUBTYPE_SPEC_MODULE = 7,        /* Module spec. */
    CAPI_SUBTYPE_SPEC_TERMINATOR = 8,    /* Terminator spec. */

    CAPI_SUBTYPE_DD_ALL = 13,            /* DD Entries (All). */
    CAPI_SUBTYPE_DD_COUPLE = 14,         /* DD Entries (Couples). */
    CAPI_SUBTYPE_DD_DATA_AREA = 15,      /* DD Entries (Data Areas). */
    CAPI_SUBTYPE_DD_DATA_OBJECT = 16,    /* DD Entries (Data Objects). */
    CAPI_SUBTYPE_DD_FLOW = 17,           /* DD Entries (Flows). */
    CAPI_SUBTYPE_DD_RELATIONSHIP = 18,   /* DD Entries (Relationships). */
    CAPI_SUBTYPE_DD_STORE = 19,          /* DD Entries (Stores). */

    CAPI_SUBTYPE_DIAG_PAD = 35,          /* Physical architecture diagram. */
    CAPI_SUBTYPE_DIAG_BD  = 36,          /* Behaviour diagram. */
    CAPI_SUBTYPE_DIAG_UCD = 37,          /* UML Use case diagram. */
    CAPI_SUBTYPE_DIAG_PD  = 38,          /* UML Package diagram. */
    CAPI_SUBTYPE_DIAG_COD = 39,          /* UML Collaboration diagram. */
    CAPI_SUBTYPE_DIAG_SQD = 40,          /* UML Sequence diagram. */
    CAPI_SUBTYPE_DIAG_CD  = 41,          /* UML Class diagram. */
    CAPI_SUBTYPE_DIAG_SCD = 42,          /* UML State chart. */
    CAPI_SUBTYPE_DIAG_ACD = 43,          /* UML Activity chart. */
    CAPI_SUBTYPE_DIAG_CPD = 44,          /* UML Component diagram. */
    CAPI_SUBTYPE_DIAG_DPD = 45,          /* UML Deployment diagram. */
    CAPI_SUBTYPE_DIAG_PFD = 47,          /* Process flow diagram. */
    CAPI_SUBTYPE_DIAG_HIER = 48,         /* Hierarchy diagram. */
    CAPI_SUBTYPE_DIAG_IDEF0 = 49,        /* IDEF0 diagram. */
    CAPI_SUBTYPE_DIAG_AID = 50,          /* AID diagram. */
    CAPI_SUBTYPE_DIAG_SAD = 51,          /* SAD diagram. */
    CAPI_SUBTYPE_DIAG_ASG = 59,           /* ASG diagram. */
    CAPI_END = 60                        /* just to mark the end of your enum */
} CAPI_SUBTYPE_E ;

CAPI_SUBTYPE_E& operator++(CAPI_SUBTYPE_E& capi)
{
  const int ranges = 2;  // you have 2 invalid ranges in your example
  int invalid[ranges][2] = {{8, 12}, {19, 34}};  // {min, max} (inclusive, exclusive)

  CAPI_SUBTYPE_E next = CAPI_SUBTYPE_NULL;

  for (int i = 0; i < ranges; i++)
    if ( capi >= invalid[i][0] && capi < invalid[i][1] ) {
      next = static_cast<CAPI_SUBTYPE_E>(invalid[i][1] + 1);
      break;
    } else {
      next = static_cast<CAPI_SUBTYPE_E>(capi + 1);
    }

  //  if ( next > CAPI_END )
    // throw an exception

  return capi = next;
}

int main()
{
  for(CAPI_SUBTYPE_E i = CAPI_SUBTYPE_NULL; i < CAPI_END; ++i)
    cout << i << endl;

  cout << endl;
}

プレインクリメント演算子のみを提供しています。ポストインクリメント演算子は後で実装できます。

于 2013-05-20T02:40:18.937 に答える
3

ブースト前処理を少し加えて、ややクリア (???) にします。

列挙型をシーケンスで定義します

#define CAPI_SUBTYPE_E_Sequence \
    (CAPI_SUBTYPE_NULL)(0)  \
    (CAPI_SUBTYPE_DIAG_DFD)(1) ...

次に、列挙型の宣言を(マクロを介して)自動化できます。

DECL_ENUM(CAPI_SUBTYPE_E) ;

インデックスを作成するテーブル

DECL_ENUM_TABLE(CAPI_SUBTYPE_E);

列挙型の数 / テーブルのサイズ

ENUM_SIZE(CAPI_SUBTYPE_E)

そしてそれにアクセスします:

ITER_ENUM_i(i,CAPI_SUBTYPE_E)

全文はこちら。

#include <boost/preprocessor.hpp>

// define your enum as (name)(value) sequence
#define CAPI_SUBTYPE_E_Sequence \
    (CAPI_SUBTYPE_NULL)(0)  /* Null subtype. */ \
    (CAPI_SUBTYPE_DIAG_DFD)(1) /* Data Flow diag. */ \
    (CAPI_SUBTYPE_DIAG_ERD)(2)  /* Entity-Relationship diag. */ \
    (CAPI_SUBTYPE_DIAG_DSD)(5) /* Data Structure diag. */ \
    (CAPI_SUBTYPE_DD_ALL)(13) /* DD Entries (All). */

//  # enums
#define ENUM_SIZE(name) \
    BOOST_PP_DIV(BOOST_PP_SEQ_SIZE(BOOST_PP_CAT(name,_Sequence)),2)

#define ENUM_NAME_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_MUL(N,2),seq)
#define ENUM_VALUE_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_INC(BOOST_PP_MUL(N,2)),seq) 

// declare Nth enum
#define DECL_ENUM_N(Z,N,seq) \
    BOOST_PP_COMMA_IF(N)   ENUM_NAME_N(N,seq) =  ENUM_VALUE_N(N,seq)

// declare whole enum
#define DECL_ENUM(name) \
    typedef enum { \
       BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_N , BOOST_PP_CAT(name,_Sequence) ) \
       } name 

DECL_ENUM(CAPI_SUBTYPE_E) ;

// declare Nth enum value
#define DECL_ENUM_TABLE_N(Z,N,seq) \
    BOOST_PP_COMMA_IF(N)   ENUM_NAME_N(N,seq)

// declare table
#define DECL_ENUM_TABLE(name) \
    static const name BOOST_PP_CAT(name,_Table) [ENUM_SIZE(name)] = { \
       BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_TABLE_N , BOOST_PP_CAT(name,_Sequence) ) \
       } 

DECL_ENUM_TABLE(CAPI_SUBTYPE_E);

#define ITER_ENUM_i(i,name)  BOOST_PP_CAT(name,_Table) [i] 

// demo 
// outputs :  [0:0] [1:1] [2:2] [3:5] [4:13]
#include <iostream>

int main() {
    for (int i=0; i<ENUM_SIZE(CAPI_SUBTYPE_E) ; i++)
        std::cout << "[" << i << ":" << ITER_ENUM_i(i,CAPI_SUBTYPE_E) << "] ";

    return 0;
}

// bonus : check enums are unique and in-order

#include <boost/preprocessor/stringize.hpp>
#include  <boost/static_assert.hpp>

      #define CHECK_ENUM_N(Z,N,seq) \
      BOOST_PP_IF( N , \
      BOOST_STATIC_ASSERT_MSG( \
            ENUM_VALUE_N(BOOST_PP_DEC(N),seq) < ENUM_VALUE_N(N,seq) , \
               BOOST_PP_STRINGIZE( ENUM_NAME_N(BOOST_PP_DEC(N),seq) ) " not < " BOOST_PP_STRINGIZE( ENUM_NAME_N(N,seq) ) ) \
               , ) ;

#define CHECK_ENUM(name) \
    namespace { void BOOST_PP_CAT(check_enum_,name) () { \
    BOOST_PP_REPEAT( ENUM_SIZE(name) , CHECK_ENUM_N , BOOST_PP_CAT(name,_Sequence) )  } }

// enum OK
CHECK_ENUM(CAPI_SUBTYPE_E)

#define Bad_Enum_Sequence \
    (one)(1)\
    (five)(5)\
    (seven)(7)\
    (three)(3)

// enum not OK : enum_iter.cpp(81): error C2338: seven not < three
CHECK_ENUM(Bad_Enum)
于 2013-05-21T17:01:49.447 に答える
3

enum答えは「いいえ、 C++03 または C++11の an の要素を反復処理することはできません」です。

enumこれで、コンパイル時に理解できる方法で an の値のセットを記述できます。

template<typename E, E... Es>
struct TypedEnumList {};

typedef TypedEnumList<
  CAPI_SUBTYPE_E,
  CAPI_SUBTYPE_NULL, // etc
  // ...
  CAPI_SUBTYPE_DIAG_ASG
> CAPI_SUBTYPE_E_LIST;

CAPI_SUBTYPE_E_LISTこれにより、値のリストをカプセル化する型が得られますenum

次に、これらを簡単に配列に入力できます。

 template<typename T, T... Es>
 std::array<T, sizeof...(Es)> GetRuntimeArray( TypedEnumList<T, Es... > ) {
   return { Es... };
 }
 auto Capis = GetRuntimeArray( CAPI_SUBTYPE_E_LIST() );

本当に必要な場合。ただし、これは、各要素のコードを生成できるというより一般的なケースの特殊なケースにすぎません。ループをenum CAPI_SUBTYPE_E直接構築する必要はありません。for

面白いことに、対応する C++11 コンパイラを使用すると、特定の要素が実際にSFINAE を使用している場合CAPI_SUBTYPE_E_LISTに特定の要素を生成するコードを記述できます。これは、サポートできる API の最新バージョンを取得し、コンパイル対象の API がより原始的である場合に (コンパイル時に) 自動劣化させることができるため、便利です。enumCAPI_SUBTYPE_E

テクニックをデモンストレーションするために、おもちゃから始めますenum

enum Foo { A = 0, /* B = 1 */ };

B=1最新バージョンの API ではコメント解除されているが、より原始的なバージョンではコメントされていないことを想像してみてください。

template<int index, typename EnumList, typename=void>
struct AddElementN: AddElementN<index-1, EnumList> {};
template<typename EnumList>
struct AddElementN<-1, EnumList, void> {
  typedef EnumList type;
};

template<typename Enum, Enum... Es>
struct AddElementN<0, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::A == Enum::A >::type >:
  AddElement<-1, TypedEnumList<Enum, A, Es...>>
{};
template<typename Enum, Enum... Es>
struct AddElementN<1, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::B == Enum::B >::type >:
  AddElement<0, TypedEnumList<Enum, B, Es...>>
{};
// specialize this for your enum to call AddElementN:
template<typename Enum>
struct BuildTypedList;
template<>
struct BuildTypedList<CAPI_SUBTYPE_E>:
  AddElementN<1, TypedEnumList<CAPI_SUBTYPE_E>>
{};
template<typename Enum>
using TypedList = typename BuildTypedList<Enum>::type;

今、私がそのように書いた場合、TypedList<CAPI_SUBTYPE_E>contains BiffBは の要素として定義されていますCAPI_SUBTYPE_E。これにより、ライブラリの複数のバージョンに対してコンパイルし、ライブラリのenum内容に応じて要素リストに異なる要素のセットを取得できます。s 要素の「最終」バージョンに対して、煩わしいボイラープレート (おそらくマクロやコード生成で簡単になる可能性があります) を維持する必要がありますがenum、コンパイル時に以前のバージョンを自動的に処理する必要があります。

残念ながら、これを機能させるには多くのメンテナンスが必要です。

最後に、これが動的であるという要件: これを動的にする唯一の実際的な方法は、API のバージョンが何であるかを認識し、enum値の異なるバッファーを公開するコードでサードパーティ API をラップすることです (私はそれを置くでしょう) std::vectorAPI のバージョンによって異なります。次に、API をロードするときに、このヘルパー ラッパーもロードします。これにより、上記の手法を使用して の要素のセットが構築されenum、これを反復処理します。

このボイラープレートの一部は、 を使用して再帰型のインデックスを作成することにより、さまざまなAddElementN型の SFINAE コードを構築するマクロなど、恐ろしいマクロを使用して簡単に作成できます。__LINE__しかし、それは恐ろしいことです。

于 2013-05-21T03:26:56.640 に答える
2

高次マクロを使用する

これが私たちのプロジェクトで使用してきたテクニックです。

概念:

アイデアは、名前と値のペアの定義を含む LISTING というマクロを生成することであり、別のマクロを引数として取ります。以下の例では、そのようなヘルパー マクロを 2 つ定義しています。'GENERATE_ENUM' で列挙型を生成し、'GENERATE_ARRAY' で反復可能な配列を生成します。もちろん、これは必要に応じて拡張できます。このソリューションは、費用対効果が最も高いと思います。概念的には、 iammilnd のソリューションと非常によく似ています。

例:

// helper macros
#define GENERATE_ENUM(key,value)       \
      key = value                      \

#define GENERATE_ARRAY(name,value)     \
       name                            \

// Since this is C++, I took the liberty to wrap everthing in a namespace. 
// This done mostly for aesthetic reasons, you don't have to if you don't want.        
namespace CAPI_SUBTYPES 
{
    //  I define a macro containing the key value pairs
    #define LISTING(m)                 \ 
       m(NONE, 0),    /* Note: I can't use NULL here because it conflicts */
       m(DIAG_DFD, 1),                 \
       m(DIAG_ERD, 2),                 \
       ...
       m(DD_ALL, 13),                  \
       m(DD_COUPLE, 14),               \
       ...
               m(DIAG_SAD, 51),                \
       m(DIAG_ASG, 59),                \

    typedef enum {
       LISTING(GENERATE_ENUM)
    } Enum;

    const Enum At[] = {
       LISTING(GENERATE_ARRAY)
    };

    const unsigned int Count = sizeof(At)/sizeof(At[0]);
}

使用法:

コードでは、次のように列挙型を参照できます。

CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::DIAG_STD;

次のように列挙を繰り返すことができます。

for (unsigned int i=0; i<CAPI_SUBTYPES::Count;  i++) {
     ...
     CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::At[i];
     ...
}

ノート:

メモリが正しく機能する場合、C++11 列挙型は独自の名前空間 (Java や C# など) に存在するため、上記の使用法は機能しません。このCAPI_SUBTYPES::Enum::FooBarのような列挙値を参照する必要があります。

于 2013-05-23T11:58:10.743 に答える
2

enumC++ では、任意の反復処理はできません。反復するには、値を何らかのコンテナーに入れる必要があります。ここで説明されているように、「列挙型クラス」を使用してそのようなコンテナーの維持を自動化できます: when-enum-just-isnt-enough-enumeration-c/184403955

于 2013-05-16T20:18:03.487 に答える
1

マクロを使用せず、実行時のオーバーヘッドが (ほとんど) ないソリューションの始まり:

#include <iostream>
#include <utility>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/find.hpp>

template<int v> using has_value = std::integral_constant<int, v>;

template<class...EnumValues>
struct better_enum
{
    static constexpr size_t size = sizeof...(EnumValues);
    using value_array = int[size];
    static const value_array& values() {
        static const value_array _values = { EnumValues::value... };
        return _values;
    }
    using name_array = const char*[size];
    static const name_array& names() {
        static const name_array _names = { EnumValues::name()... };
        return _names;
    }


    using enum_values = boost::mpl::vector<EnumValues...>;

    struct iterator {
        explicit iterator(size_t i) : index(i) {}

        const char* name() const {
            return names()[index];
        }
        int value() const {
            return values()[index];
        }
        operator int() const {
            return value();
        }

        void operator++() {
            ++index;
        }
        bool operator==(const iterator& it) const {
            return index == it.index;
        }
        bool operator!=(const iterator& it) const {
            return index != it.index;
        }
        const iterator& operator*() const {
            return *this;
        }
    private:
        size_t index;
    };
    friend std::ostream& operator<<(std::ostream& os, const iterator& iter)
    {
        os << "{ " << iter.name() << ", " << iter.value() << " }";
        return os;
    }

    template<class EnumValue>
    static iterator find() {
        using iter = typename boost::mpl::find<enum_values, EnumValue>::type;
        static_assert(iter::pos::value < size, "attempt to find a value which is not part of this enum");
        return iterator { iter::pos::value };
    }

    static iterator begin() {
        return iterator { 0 };
    }

    static iterator end() {
        return iterator { size };
    }

};

struct Pig : has_value<0> { static const char* name() { return "Pig";} };
struct Dog : has_value<7> { static const char* name() { return "Dog";} };
struct Cat : has_value<100> { static const char* name() { return "Cat";} };
struct Horse : has_value<90> { static const char* name() { return "Horse";} };

struct Monkey : has_value<1000> { static const char* name() { return "Monkey";} };

using animals = better_enum<
Pig,
Dog,
Cat,
Horse
>;

using namespace std;

auto main() -> int
{
    cout << "size : " << animals::size << endl;
    for (auto v : animals::values())
    cout << v << endl;

    for (auto v : animals::names())
    cout << v << endl;

    cout << "full iteration:" << endl;
    for (const auto& i : animals())
    {
        cout << i << endl;
    }

    cout << "individials" << endl;
    auto animal = animals::find<Dog>();
    cout << "found : " << animal << endl;
    while (animal != animals::find<Horse>()) {
        cout << animal << endl;
        ++animal;
    }

// will trigger the static_assert    auto xx = animals::find<Monkey>();

    return 0;
}

出力:

size : 4
0
7
100
90
Pig
Dog
Cat
Horse
full iteration:
{ Pig, 0 }
{ Dog, 7 }
{ Cat, 100 }
{ Horse, 90 }
individials
found : { Dog, 7 }
{ Dog, 7 }
{ Cat, 100 }
于 2015-06-24T23:58:14.217 に答える
0

このタイプの構造を使用して、独自の列挙型を定義しています。

#include <boost/unordered_map.hpp>

namespace enumeration
{

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

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

      iterator begin() const
      {
         return entries_.begin();
      }

      iterator end() const
      {
         return entries_.end();
      }

      iterator begin()
      {
         return entries_.begin();
      }

      iterator end()
      {
         return entries_.end();
      }

      void register_e(int val, std::string const & desc)
      {
         storage_.insert(std::make_pair(val, desc));
         entries_.insert(val);
      }
   protected:
      kv_storage_t storage_;
      entries_t entries_;
   };

   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 register_e((int)K, V);
#define QENUM_ENTRY_I(K, I, V, N)  K = I, N register_e((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()        \
   {


QBEGIN_ENUM(test_t,
   QENUM_ENTRY(test_entry_1, "number uno",
   QENUM_ENTRY_I(test_entry_2, 10, "number dos",
   QENUM_ENTRY(test_entry_3, "number tres",
QEND_ENUM(test_t)))))


int _tmain(int argc, _TCHAR* argv[])
{
   BOOST_FOREACH(int x, enumeration::enumerator<test_t>::instance())
      std::cout << enumeration::enumerator<test_t>::instance().name(x) << "=" << x << std::endl;
   return 0;
}

storage_また、タイプをboost::bimap双方向に対応する int <==> string に置き換えることもできます

于 2013-05-21T17:11:10.160 に答える
0

もう 1 つのアプローチを次に示します。ボーナスの 1 つは、次のように列挙型の値を省略した場合、コンパイラが警告を表示する可能性があることですswitch

template<typename T>
void IMP_Apply(const int& pSubtype, T& pApply) {
    switch (pSubtype) {
        case CAPI_SUBTYPE_NULL :
        case CAPI_SUBTYPE_DIAG_DFD :
        case CAPI_SUBTYPE_DIAG_ERD :
        case CAPI_SUBTYPE_DIAG_STD :
        case CAPI_SUBTYPE_DIAG_STC :
        case CAPI_SUBTYPE_DIAG_DSD :
        case CAPI_SUBTYPE_SPEC_PROCESS :
        case CAPI_SUBTYPE_SPEC_MODULE :
        case CAPI_SUBTYPE_SPEC_TERMINATOR :
        case CAPI_SUBTYPE_DD_ALL :
        case CAPI_SUBTYPE_DD_COUPLE :
        case CAPI_SUBTYPE_DD_DATA_AREA :
        case CAPI_SUBTYPE_DD_DATA_OBJECT :
        case CAPI_SUBTYPE_DD_FLOW :
        case CAPI_SUBTYPE_DD_RELATIONSHIP :
        case CAPI_SUBTYPE_DD_STORE :
        case CAPI_SUBTYPE_DIAG_PAD :
        case CAPI_SUBTYPE_DIAG_BD :
        case CAPI_SUBTYPE_DIAG_UCD :
        case CAPI_SUBTYPE_DIAG_PD :
        case CAPI_SUBTYPE_DIAG_COD :
        case CAPI_SUBTYPE_DIAG_SQD :
        case CAPI_SUBTYPE_DIAG_CD :
        case CAPI_SUBTYPE_DIAG_SCD :
        case CAPI_SUBTYPE_DIAG_ACD :
        case CAPI_SUBTYPE_DIAG_CPD :
        case CAPI_SUBTYPE_DIAG_DPD :
        case CAPI_SUBTYPE_DIAG_PFD :
        case CAPI_SUBTYPE_DIAG_HIER :
        case CAPI_SUBTYPE_DIAG_IDEF0 :
        case CAPI_SUBTYPE_DIAG_AID :
        case CAPI_SUBTYPE_DIAG_SAD :
        case CAPI_SUBTYPE_DIAG_ASG :
            /* do something. just `applying`: */
            pApply(static_cast<CAPI_SUBTYPE_E>(pSubtype));
            return;
    }

    std::cout << "Skipped: " << pSubtype << '\n';
}

template<typename T>
void Apply(T& pApply) {
    const CAPI_SUBTYPE_E First(CAPI_SUBTYPE_NULL);
    const CAPI_SUBTYPE_E Last(CAPI_SUBTYPE_DIAG_ASG);

    for (int idx(static_cast<int>(First)); idx <= static_cast<int>(Last); ++idx) {
        IMP_Apply(idx, pApply);
    }
}

int main(int argc, const char* argv[]) {
    class t_apply {
    public:
        void operator()(const CAPI_SUBTYPE_E& pSubtype) const {
            std::cout << "Apply: " << static_cast<int>(pSubtype) << '\n';
        }
    };
    t_apply apply;
    Apply(apply);
    return 0;
}
于 2013-05-19T04:51:58.290 に答える
0

列挙型では反復が許可されないため、列挙型とその値の範囲の代替表現を作成する必要があります。

私が取るアプローチは、クラスに埋め込まれた単純なテーブル ルックアップです。問題は、API がその列挙型を新しいエントリで変更するときに、このクラスのコンストラクターも更新する必要があることです。

使用する単純なクラスは、テーブルを構築するためのコンストラクターと、テーブルを反復処理するためのいくつかのメソッドで構成されます。アイテムを追加するときにテーブル サイズに問題があるかどうかも知りたいので、デバッグ モードでassert ()を発行するマクロを使用できます。assert()以下のソース例では、基本的な一貫性チェックのメカニズムを提供するために、プリプロセッサを使用してデバッグ コンパイルの有無と assert が含まれているかどうかをテストしています。

PJ Plauger の著書 Standard C Library で見た、ANSI 文字操作に単純なルックアップ テーブルを使用するというアイデアを借りました。このテーブルでは、文字を使用してテーブルにインデックスが付けられます。

このクラスを使用するには、forループを使用してテーブル内の値のセットを反復処理する次のようなことを行います。ループの本体内では、列挙値でやりたいことは何でもします。

CapiEnum myEnum;

for (CAPI_SUBTYPE_E jj = myEnum.Begin(); !myEnum.End(); jj = myEnum.Next()) {
     // do stuff with the jj enum value
}

CAPI_SUBTYPE_NULLこのクラスは値を列挙するため、列挙の最後に達した場合に の値を返すことを任意に選択しました。したがって、テーブル ルックアップ エラーの場合の戻り値は有効な範囲内ですが、依存することはできません。End()したがって、反復の終わりに達したかどうかを確認するために、メソッド のチェックを行う必要があります。また、オブジェクトの構築を行った後、m_bTableErrorデータ メンバーをチェックして、構築中にエラーが発生したかどうかを確認できます。

クラスのソース例は次のとおりです。 API の列挙値が変更されたときに、コンストラクターを更新する必要があります。 残念ながら、更新列挙型のチェックを自動化するためにできることはあまりありませんが、テーブルが十分に大きく、テーブルに入れられる列挙型の値がテーブルサイズの範囲。

class CapiEnum {
public:
    CapiEnum (void);                                // constructor
    CAPI_SUBTYPE_E  Begin (void);                   // method to call to begin an iteration
    CAPI_SUBTYPE_E  Next (void);                    // method to get the next in the series of an iteration
    bool            End (void);                     // method to indicate if we have reached the end or not
    bool            Check (CAPI_SUBTYPE_E value);   // method to see if value specified is in the table
    bool  m_TableError;
private:
    static const int m_TableSize = 256;    // set the lookup table size
    static const int m_UnusedTableEntry = -1;
    int   m_iIterate;
    bool  m_bEndReached;
    CAPI_SUBTYPE_E  m_CapiTable[m_TableSize];
};

#if defined(_DEBUG)
#if defined(assert)
#define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : assert(((capi) < m_TableSize) && ((capi) > m_UnusedTableEntry)))
#else
#define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : (m_TableError = true))
#endif
#else
#define ADD_CAPI_ENUM_ENTRY(capi) (m_CapiTable[(capi)] = (capi))
#endif

CapiEnum::CapiEnum (void) : m_bEndReached(true), m_iIterate(0), m_TableError(false)
{
    for (int iLoop = 0; iLoop < m_TableSize; iLoop++) m_CapiTable[iLoop] = static_cast <CAPI_SUBTYPE_E> (m_UnusedTableEntry);
    ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_NULL);
    // .....
    ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_DIAG_ASG);
}

CAPI_SUBTYPE_E CapiEnum::Begin (void)
{
    m_bEndReached = false;
    for (m_iIterate = 0; m_iIterate < m_TableSize; m_iIterate++) {
        if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
    }
    m_bEndReached = true;
    return CAPI_SUBTYPE_NULL;
}

CAPI_SUBTYPE_E CapiEnum::Next (void)
{
    if (!m_bEndReached) {
        for (m_iIterate++; m_iIterate < m_TableSize; m_iIterate++) {
            if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
        }
    }
    m_bEndReached = true;
    return CAPI_SUBTYPE_NULL;
}

bool CapiEnum::End (void)
{
    return m_bEndReached;
}

bool CapiEnum::Check (CAPI_SUBTYPE_E value)
{
    return (value >= 0 && value < m_TableSize && m_CapiTable[value] > m_UnusedTableEntry);
}

また、必要に応じて、反復の現在の値を取得するメソッドを追加することもできます。Current() メソッドは、次にインクリメントするのではなく、現在の反復インデックスを使用して、現在の位置から検索を開始することに注意してください。したがって、現在の位置が有効な値である場合は、それを返すだけです。それ以外の場合は、最初の有効な値を見つけます。または、インデックスが指す現在のテーブル値を返すだけにして、値が無効な場合はエラー インジケーターを設定することもできます。

CAPI_SUBTYPE_E CapiEnum::Current (void)
{
    if (!m_bEndReached) {
        for (m_iIterate; m_iIterate < m_TableSize; m_iIterate++) {
            if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
        }
    }
    m_bEndReached = true;
    return CAPI_SUBTYPE_NULL;
}
于 2013-05-19T13:18:56.400 に答える
0

それらを配列または他のコンテナーに入れて、それを反復処理します。列挙型を変更する場合は、それらをコンテナーに入れるコードを更新する必要があります。

于 2013-05-16T20:11:14.053 に答える