2011-02-11 28 views
36

私はバイナリファイルからenumの値を読んでいて、その値が本当にenum値の一部であるかどうかをチェックしたいと思います。どうしたらいいですか?enumの値が有効かどうかを確認する方法は?

#include <iostream> 

enum Abc 
{ 
    A = 4, 
    B = 8, 
    C = 12 
}; 

int main() 
{ 
    int v1 = 4; 
    Abc v2 = static_cast<Abc>(v1); 

    switch (v2) 
    { 
     case A: 
      std::cout<<"A"<<std::endl; 
      break; 
     case B: 
      std::cout<<"B"<<std::endl; 
      break; 
     case C: 
      std::cout<<"C"<<std::endl; 
      break; 
     default : 
      std::cout<<"no match found"<<std::endl; 
    } 
} 

私はswitch演算子を使用する必要がありますか、それとも良い方法がありますか?

EDIT

私は列挙型の値が設定されていると、残念ながら、私はそれらを変更することはできません。さらに悪いことに、彼らは連続していない(それらの値はなど、75,76,80,85,90,95,100、0を行く)

+3

任意の列挙型は単なる数値なので、それを確認するより良い方法はないと思います。データ型にはより厳密な構造を定義するべきでしょう。 – Rizo

答えて

21

enum値は、以下の標準規則で定義されている範囲[A、B]にある場合、C++で有効です。したがって、enum X { A = 1, B = 3 }の場合、2の値は有効な列挙値と見なされます。

は、標準の7.2/6検討:強度Eminが最小列挙であり、EMAXが最も大きい列挙について

、列挙の値BMAXする範囲Bminの基になる型の値であり、ここで、bminおよびbmaxは、それぞれ、eminおよびemaxを格納することができる最小のビットフィールドの最小値および最大値である。列挙子のいずれかで定義されていない値を持つ列挙型を定義することは可能です。

C++には逆戻りはありません。 1つのアプローチは、列挙型の値を配列に追加してリストアし、変換を行い、失敗した場合に例外をスローするラッパーを作成することです。

詳細については、intを列挙する方法については、Similar Questionを参照してください。

+1

標準の見積もりを誤解した場合、有効な値に '[A、B]'以上あります。 –

+5

実際、たとえば値が1および5の場合、後者は少なくとも3ビットを必要とするため、6および7も列挙子の有効な値になります。 – visitor

+0

@Matthieu:ありがとう、答えを編集しました。 – Leonid

9

たぶん、このような列挙型を使用します。

enum MyEnum 
{ 
A, 
B, 
C 
}; 

をチェックします

if (v2 >= A && v2 <= C) 

列挙型定数の値を指定しない場合、値はゼロから始まり、リストを移動するたびに1ずつ増加します。例えば、所与 enum MyEnumType { ALPHA, BETA, GAMMA }; ALPHAは0の値を有し、何より良い方法がない、BETAは1の値を有し、GAMMA言語について言えば2

+1

私はこれの単純さが好きで、常にSOMETYPE_UNKNOWNとして列挙型の最初の項目を定義し、SOMETYPE_MAXとして最後の項目を定義することによってそれを拡張しました。その後、テストは常にAssertTrue(v2> = SOMETYPE_UNKNOWN && v2 <= SOMETYPE_MAX)になります。もちろん、UNKNOWNの後でMAXの前に項目を追加するだけです。 –

+0

私はあなたのアイデアを最初は理解していませんでしたが、それは確かに優れた、ゼロメンテナンスのトリックです! – pfabri

2

の値を有する、列挙値は、コンパイル時に存在しますプログラムでそれらを列挙する方法はありません。しかし、よく考えられたインフラストラクチャでは、すべての値を数回リストすることを避けることができます。 (すでにそのヘッダ内に存在するいくつかのより多くの設備を使用して)

#include "enumFactory.h" 

#define ABC_ENUM(XX) \ 
    XX(A,=4) \ 
    XX(B,=8) \ 
    XX(C,=12) \ 

DECLARE_ENUM(Abc,ABC_ENUM) 

int main() 
{ 
    int v1 = 4; 
    Abc v2 = static_cast<Abc>(v1); 

    #define CHECK_ENUM_CASE(name,assign) case name: std::cout<< #name <<std::endl; break; 
    switch (v2) 
    { 
     ABC_ENUM(CHECK_ENUM_CASE) 
     default : 
      std::cout<<"no match found"<<std::endl; 
    } 
    #undef CHECK_ENUM_CASE 
} 

かさえ:Easy way to use variables of enum types as string in C?

あなたのサンプルは、次にとして提供「enumFactory.h」を使用して書き換えることができますを参照してください

#include "enumFactory.h" 

#define ABC_ENUM(XX) \ 
    XX(A,=4) \ 
    XX(B,=8) \ 
    XX(C,=12) \ 

DECLARE_ENUM(Abc,ABC_ENUM) 
DEFINE_ENUM(Abc,ABC_ENUM) 

int main() 
{ 
    int v1 = 4; 
    Abc v2 = static_cast<Abc>(v1); 
    const char *name = GetString(v2); 
    if (name[0]==0) name = "no match found"; 
    std::cout << name << std::endl; 
} 
7

私がこれまで「簡単に」見つけた唯一の方法は、列挙型のソートされた配列を作成して(マクロ)作成してチェックすることでした。

enumは、指定された値を持つ複数の列挙子を持つ可能性があるため、enumでトリックが失敗します。

本当に迷惑な問題です。

3
C++のための

マネージ拡張には、次の構文をサポートしています。

enum Abc 
{ 
    A = 4, 
    B = 8, 
    C = 12 
}; 

Enum::IsDefined(Abc::typeid, 8); 

参考:C++ 11ではMSDN「Managed Extensions for C++ Programming

+0

私は "managed C++"が何であるか分かりませんが、C++ではないと確信していますか? [this](http://msdn.microsoft.com/en-us/library/system.enum.isdefined%28v=vs.110%29。aspx)はCのように見える# –

+3

@BЈовић: 'managed C++'は 'C++ 'のMicrosoft変種で、' .NETフレームワーク 'のライブラリを使うことができます。このように、 'c'には '::'演算子が 'c#'で定義されていないので、 '' C++ ''のように見えます。 – Stefan

+0

@BЈовиMan Managed Extensions C++プロジェクトでコードを試しましたか? C++プロジェクトの1つでも同様のコードを使用しています。具体的には、Enum :: IsDefined()メソッドを使用します。 – Brett

4

テンプレートパラメータとして、あなたの列挙型の値をリストする用意がある場合は、より良い方法があります。これを良いものとして見ることができ、異なるコンテキストで有効なenum値のサブセットを受け入れることができます。外部ソースからコードを解析するときに便利です。

以下の例では、トランケーションの問題を避けるために、IntTypeに関連するEnumTypeの基になるタイプの静的アサーションを追加することができます。運動として残した。

ヘッダー:

ちょっとネクロ
#include <stdio.h> 

template<typename EnumType, EnumType... Values> class EnumCheck; 

template<typename EnumType> class EnumCheck<EnumType> 
{ 
public: 
    template<typename IntType> 
    static bool constexpr is_value(IntType) { return false; } 
}; 

template<typename EnumType, EnumType V, EnumType... Next> 
class EnumCheck<EnumType, V, Next...> : private EnumCheck<EnumType, Next...> 
{ 
    using super = EnumCheck<EnumType, Next...>; 

public: 
    template<typename IntType> 
    static bool constexpr is_value(IntType v) 
    { 
     return v == static_cast<IntType>(V) || super::is_value(v); 
    } 
}; 

enum class Test { 
    A = 1, 
    C = 3, 
    E = 5 
}; 

using TestCheck = EnumCheck<Test, Test::A, Test::C, Test::E>; 

void check_value(int v) 
{ 
    if (TestCheck::is_value(v)) 
     printf("%d is OK\n", v); 
    else 
     printf("%d is not OK\n", v); 
} 

int main() 
{ 
    for (int i = 0; i < 10; ++i) 
     check_value(i); 
} 
+0

'int v'の値はコンパイル時には分かりませんので、実行時に 'is_value'を実行する必要があります。これは、すべての種類の再帰的な関数呼び出しをもたらし、単純なswitch文やすべての値の配列に比べて非常に効率的ではないでしょうか?列挙型の値をすべて列挙しなければならないので、このアプローチで何かを得ているようなわけではありません。それとも私はここで何かを逃したのですか –

+0

@InnocentBystanderこれらはすべて 'constexpr'関数なので、最適化のためのスコープがたくさんあります。関数は再帰的でもありません。それは同じ名前を持つ関数の連鎖です。上記の例を使ったいくつかのクイックテストでは、gcc 5.4は、テンプレートバージョンとスイッチバージョンの短い命令を生成します。 Clang 3.8は、テンプレートバージョンの2つの命令長です。結果は、値の数と値が連続しているかどうかによって異なります。特にプロトコルデコードを行う際には、期待するコードを1行に書くことが大きな利点です。 – janm

+1

あなたは正しいです。残念ながら "再帰的"ではありませんが、関数呼び出しは連鎖しています。コンパイラがすべてのものを最適化できるというのは面白いことです。そして、3歳の答えをフォローアップしてくれてありがとう:) –

2

、しかし...は、最初/最後の列挙値(正確な検査を行うためにjanmのアイデアを組み合わせることができます)、C++ 11にint型の範囲チェックを行います

namespace chkenum 
{ 
    template <class T, T begin, T end> 
    struct RangeCheck 
    { 
    private: 
     typedef typename std::underlying_type<T>::type val_t; 
    public: 
     static 
     typename std::enable_if<std::is_enum<T>::value, bool>::type 
     inrange(val_t value) 
     { 
      return value >= static_cast<val_t>(begin) && value <= static_cast<val_t>(end); 
     } 
    }; 

    template<class T> 
    struct EnumCheck; 
} 

#define DECLARE_ENUM_CHECK(T,B,E) namespace chkenum {template<> struct EnumCheck<T> : public RangeCheck<T, B, E> {};} 

template<class T> 
inline 
typename std::enable_if<std::is_enum<T>::value, bool>::type 
testEnumRange(int val) 
{ 
    return chkenum::EnumCheck<T>::inrange(val); 
} 

列挙宣言:

enum MinMaxType 
{ 
    Max = 0x800, Min, Equal 
}; 
DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal); 

使用:

bool r = testEnumRange<MinMaxType>(i); 

上記の主な違いは、テスト機能が列挙型自体にのみ依存することです。それを行うには

0

さらに別の方法:

#include <algorithm> 
#include <iterator> 
#include <iostream> 

template<typename> 
struct enum_traits { static constexpr void* values = nullptr; }; 

namespace detail 
{ 

template<typename T> 
constexpr bool is_value_of(int, void*) { return false; } 

template<typename T, typename U> 
constexpr bool is_value_of(int v, U) 
{ 
    using std::begin; using std::end; 

    return std::find_if(begin(enum_traits<T>::values), end(enum_traits<T>::values), 
     [=](auto value){ return value == static_cast<T>(v); } 
    ) != end(enum_traits<T>::values); 
} 

} 

template<typename T> 
constexpr bool is_value_of(int v) 
{ return detail::is_value_of<T>(v, decltype(enum_traits<T>::values) { }); } 

//////////////////// 
enum Abc { A = 4, B = 8, C = 12 }; 

template<> 
struct enum_traits<Abc> { static constexpr auto values = { A, B, C }; }; 
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values; 

enum class Def { D = 1, E = 3, F = 5 }; 

int main() 
{ 
    std::cout << "Abc:"; 
    for(int i = 0; i < 10; ++i) 
     if(is_value_of<Abc>(i)) std::cout << " " << i; 
    std::cout << std::endl; 

    std::cout << "Def:"; 
    for(int i = 0; i < 10; ++i) 
     if(is_value_of<Def>(i)) std::cout << " " << i; 
    std::cout << std::endl; 

    return 0; 
} 

このアプローチIMHOの「醜い」の部分を定義する必要がされています

decltype(enum_traits<Abc>::values) enum_traits<Abc>::values 

あなたがマクロに対向していない場合は、ラップすることができますマクロ内:

#define REGISTER_ENUM_VALUES(name, ...) \ 
template<> struct enum_traits<name> { static constexpr auto values = { __VA_ARGS__ }; }; \ 
decltype(enum_traits<name>::values) enum_traits<name>::values; 
関連する問題