18

C++で列挙型の列挙型を持つことは可能ですか?私は次のようなものを持っている必要があります:

エラーの種類:

  • Type1
  • タイプ2
  • Type3

タイプ1:

  • cause1
  • cause2

タイプ2:

  • cause3
  • cause4

タイプ3:

  • cause5
  • cause6

これらはそれぞれ整数値です。これらは、通信プロトコルスタックで使用されることになっています。受信側では、受信側は受信した値からエラーのタイプと原因をデコードする必要があります。列挙型を使用できない場合、それを行うための最良の方法は何でしょうか?

4

4 に答える 4

9

列挙型の列挙が何を意味するのかさえわかりません。しかし、これを処理する通常の方法は、単一の列挙型で範囲を定義することです。

enum Errors
{
    type1 = 0x000,
    cause1,
    cause2,

    type2 = 0x100,
    cause3,
    cause4,
    ...
    causeMask = 0xFF,
    typeMask  = 0xFF00
};

または、個別の列挙型を個別の言葉で単純に定義し、 unsigned(またはunsigned short、または何でも)使用し、さまざまな原因に対して少しキャストします。

どのようなソリューションを採用したとしても、それをクラスにカプセル化して、クライアント コードで処理する必要があるのはerrorType() andだけerrorCause()です。errorCause()エラータイプ値のテンプレートになることさえあります。(しかし、どこかで、型の値ごとに明示的な特殊化が必要になるでしょう。そうしないと、コンパイラは値を原因型にマップする方法がわからないからです。)

于 2013-03-15T09:41:56.613 に答える
4

ジェリーが言ったように、それは直接不可能です。これを解決する1つの方法は、2つの列挙型を持つことです。1つはカテゴリ用、もう1つはサブカテゴリ用です。

ただし、georgeslが述べたように、プロトコルでこれを行うのはおそらく危険です。列挙値を明示的に定義する必要があります。

struct Error
{
   enum Type {
      UNKNOWNTYPE = 0,
      TYPE1 = 1,
      TYPE2 = 2,
      TYPE3 = 3
   };
   enum Subtype {
      UNKNOWNSUBTYPE = 0,
      // subtype for error type 1
      CAUSE1 = 1001,
      CAUSE2 = 1002,
      CAUSE3 = 1003,
      // subtype for error type 2
      CAUSE4 = 2001,
      CAUSE5 = 2002
   };

   Type type;
   Subtype subtype;
};

int main()
{
   Error error;
   error.type = Error::TYPE1;
   error.subtype = Error::CAUSE1;
}

将来の拡張のために、番号を賢く選択するようにしてください。

更新:例を実際に機能させました。

代替の、よりタイプセーフなソリューション:

struct ErrorType
{
   enum type {
      UNKNOWNTYPE = 0,
      TYPE1 = 1,
      TYPE2 = 2,
      TYPE3 = 3
   };
};

struct ErrorSubtype
{
   enum type {
      UNKNOWNSUBTYPE = 0,
      // subtype for error type 1
      CAUSE1 = 1001,
      CAUSE2 = 1002,
      CAUSE3 = 1003,
      // subtype for error type 2
      CAUSE4 = 2001,
      CAUSE5 = 2002
   };
};

struct Error
{
   ErrorType::type type;
   ErrorSubtype::type subtype;
};

int main()
{
   Error error;
   error.type = ErrorType::TYPE1;
   error.subtype = ErrorSubtype::CAUSE1;
}
于 2013-03-15T08:52:51.517 に答える
3

これを行うことはお勧めしません。エラーに関する情報を含む明示的なエラータイプを使用することをお勧めします (文字列などを追加できます)。また、これはあまり型安全ではありません。ジェームズの回答も参照してください。

しかし、とにかく、これは邪悪なマクロ バージョンです。

#define DECL_ERROR_TYPE(errorType, value) , errorType = value << 16
#define DECL_ERROR(errorType, cause, value) , errorType##_##cause = (errorType + value)

#define GET_ERROR_TYPE(error) (error & 0xFFFF0000)

enum Error
{
NoError = 0
DECL_ERROR_TYPE(Type1, 1)
DECL_ERROR(Type1, Cause1, 1)
DECL_ERROR(Type1, Cause2, 2)

DECL_ERROR_TYPE(Type2, 2)
DECL_ERROR(Type2, Cause1, 1)

DECL_ERROR_TYPE(Type3, 3)
DECL_ERROR(Type3, Cause1, 1)
DECL_ERROR(Type3, Cause2, 2)
};

これにより、次のように使用できます。

Error err1 = Type1_Cause1;

if(Type1 == GET_ERROR_TYPE(err1))
    return 0; // Works
于 2013-03-15T09:53:56.717 に答える
0

列挙型の使用に耐えられませんでした。したがって、明示的な型を使用した別の答えがあります。完全ではありませんが、正しい方向性を示しており、説明を追加するなどの可能な拡張が含まれています。

宣言のコードは次のとおりです。

struct Error
{
public:
    struct ErrorType
    {
        int _code;
        ErrorType(int code) : _code(code << 16) {}
    };
private:
    friend struct Errors;
    ErrorType _type;
    int _code;

    Error(ErrorType type, int causeCode)
        : _type(type), _code(causeCode)
    {
    }

    static std::map<int, Error> _errors;
public:
    Error() : _type(-1), _code(-1) {}
    static Error FromCode(int code) { return _errors[code]; }

    bool IsOfType(const ErrorType& type )
    {
        return _type._code == type._code;
    }

    operator int()
    {
        return _code | _type._code;
    }

    bool operator == (Error const& other) const
    {
        return _code == other._code && _type._code == other._type._code;
    }

    bool operator != (Error const& other) const
    {
        return _code != other._code || _type._code != other._type._code;;
    }
};

std::map<int, Error> Error::_errors;

struct Errors
{
#define BEGIN_TYPE(type, code) struct type : Error::ErrorType { type() : ErrorType(code) {} typedef Errors::##type CurrentType;
#define CAUSE(cause, code) struct cause : Error { cause() : Error(CurrentType(),code) { Error::_errors[*this] = *this; } };
#define END_TYPE() };

    // first type is coded manually to show what the macros do...
    struct Type1 : Error::ErrorType
    {
        Type1() : ErrorType(1) { }
        typedef Errors::Type1 CurrentType;

        struct Cause1 : Error
        {
            Cause1() : Error(CurrentType(),1) { Error::_errors[*this] = *this; }
        };

        struct Cause2 : Error
        {
            Cause2() : Error(CurrentType(),2) { Error::_errors[*this] = *this; }
        };
    };

    BEGIN_TYPE(Type2, 2)    
    CAUSE(Cause1, 1)
    CAUSE(Cause2, 2)
    END_TYPE()
};

そして、ここにいくつかの使用例があります:

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

    Error err = Errors::Type1::Cause1();

    Q_ASSERT( err.IsOfType(Errors::Type1()) );
    Q_ASSERT( Errors::Type1::Cause1() == Errors::Type1::Cause1() );
    Q_ASSERT( Errors::Type1::Cause1() != Errors::Type2::Cause1() );

    int code = err;
    qDebug() << code;
    Q_ASSERT( Error::FromCode(code) == Errors::Type1::Cause1() );
    Q_ASSERT( Error::FromCode(code) != Errors::Type2::Cause1() );
    Q_ASSERT( Error::FromCode(code).IsOfType(Errors::Type1()) );

    return a.exec();
}

これは完全な解決策ではありませんが、より明示的な方法でこれを処理する方法を示しています。改善できることはたくさんあります...

于 2013-03-15T15:26:03.210 に答える