2013-03-15 34 views
14

Ist es möglich Enum von Enums in C++ zu haben. Ich habe etwas haben, wie:Ist es möglich, Enum von enums in C++ zu haben?

Fehlertypen:

  • Typ1
  • Typ2
  • Typ3

Typ1:

  • Ursache 1
  • Ursache 2

Typ2:

  • cause3
  • cause4

Typ3:

  • cause5
  • cause6
  • 012.

Jeder dieser Werte sind ganzzahlige Werte. Sie sollen in einem Kommunikationsprotokollstapel verwendet werden. Auf der Empfangsseite muss der Empfänger Art und Ursache des Fehlers anhand der empfangenen Werte dekodieren. Wenn Enums nicht verwendet werden können, was wäre der beste Weg, dies zu tun?

+10

Entschuldigung, aber die Antwort ist nein - Sie müssen einen anderen Weg finden, Dinge zu tun. Eine typische besteht darin, den Typ in eine Anzahl von oberen Bits in der Anzahl und die Ursache in eine Anzahl von niedrigeren Bits (z. B. 16-Bit-Wert, jeweils 8 Bit) zu codieren. –

+0

Es ist nicht möglich, enum von enums zu haben, aber Sie könnten Ihre Daten darstellen, indem Sie den Typ und die Ursache getrennt entweder als Teil einer Struktur angeben oder bestimmte Bits für jedes der Felder zuweisen. – Tuxdude

+1

selbst wenn Sie eine enum von enum haben könnten, da dort das Programm von zwei verschiedenen Maschinen ausgeführt wird, kann der Wert derselben Sub-enum (z. B. Type1Cause1) unterschiedlich instanziiert werden. Ist das nicht gefährlich? – lucasg

Antwort

2

Wie Jerry sagte, ist es nicht direkt möglich. Eine Möglichkeit, dies zu lösen, ist, zwei Enums zu haben. Eine für die Kategorie und eine für die Unterkategorie.

Wie georgesl jedoch gesagt hat, kann es wohl gefährlich sein, dies in einem Protokoll zu tun. Sie sollten auf jeden Fall explizit die ENUM-Werte definieren:

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

Achten Sie darauf, die Zahlen mit Bedacht für zukünftige Erweiterungen zu wählen.

Update: machte das Beispiel tatsächlich funktioniert.

Alternative, mehr typsichere Lösung:

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

Benötige ich in diesem Fall den Enum-Typ, da er bereits im Subtyp codiert ist? – sajas

+0

Ich habe meine Antwort aktualisiert, damit es tatsächlich funktioniert. – Arne

+0

Aber das Problem mit diesem Design ist, dass ich einen Fehlertyp mit einem falschen Subtype, dh verbinden kann. Ich kann TYPE1 mit URSACHE4 verbinden, richtig? – sajas

7

Ich bin nicht einmal sicher, was eine Enumeration von Aufzählungen bedeuten würde. Aber die übliche Art und Weise des Umgangs mit dieser ist entweder Bereiche zu definieren, in einen einzigen ENUM:

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

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

Oder einfach getrennte Aufzählungen zu definieren, in einzelnen Worten und verwenden unsigned (oder unsigned short, oder was auch immer) und ein bisschen Casting für die verschiedenen Ursachen.

Unabhängig von der gewählten Lösung, würde ich es in eine Klasse kapseln, so dass Client-Code muss nur mit errorType() und errorCause() befassen; errorCause() könnte sogar eine Vorlage auf der Fehlertyp Wert sein. (Aber irgendwo benötigen Sie explizite Spezialisierungen für jeden Typ Wert, weil der Compiler nicht anders wissen, wie Wert zugeordnet werden, um Typ zu verursachen.)

+0

Richtig, irgendwie den Typ in die Ursache einbetten ist der einzige Weg, um sicherzustellen, dass Sie nicht die falschen Typen und Ursachen mischen. Und das ist der einfachste Weg, dies zu tun. –

3

Ich würde nicht empfehlen, dies zu tun. Lieber einen expliziten Fehlertyp verwenden, der Informationen über Fehler enthält (Sie könnten Strings usw. hinzufügen). Auch das ist nicht sehr typsicher. Siehe auch James Antwort.

Aber wie auch immer hier ist die böse Makro-Version:

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

Auf diese Weise können Sie es wie folgt verwenden:

Error err1 = Type1_Cause1; 

if(Type1 == GET_ERROR_TYPE(err1)) 
    return 0; // Works 
0

Ich kann es einfach nicht ertragen Aufzählungen verwenden. Also habe ich eine andere Antwort mit expliziten Typen. Es ist nicht vollständig, sondern zeigt die richtige Richtung, und enthält die mögliche Erweiterung der Zugabe Beschreibungen usw.

Heres der Code für die Erklärung:

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

Und hier sind einige Beispiele für den Gebrauch:

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

Es ist keine perfekte Lösung, aber zeigt, wie dies auf eine explizitere Weise behandelt werden kann. Es gibt viele Verbesserungen, die man machen kann ...

Verwandte Themen