2013-03-15 87 views
14

是否有可能枚舉的枚舉在C++中。 我必須有這樣的:是否有可能在enum中枚舉枚舉?

錯誤類型:

  • 的Type1
  • 2型
  • 的Type3

類型1:

  • 原因1
  • 原因2

類型2:

  • cause3
  • cause4

的Type3:

  • cause5
  • cause6

這些都是整數值。它們應該被用在通信協議棧中。在接收端,接收器必須根據接收到的值來解碼錯誤的類型和原因。如果枚舉不能使用,那麼最好的方法是什麼?

+10

對不起,但答案是否定的 - 你需要找到一種不同的方式來做事情。一種典型的方法是在數字中的某些高位中編碼該類型,並且在一些低位中(例如,16位值,每個8位)導致該類型。 – 2013-03-15 08:32:36

+0

枚舉的枚舉是不可能的,但是可以通過將類型和原因分別作爲結構的一部分或爲每個字段分配特定位來表示數據。 – Tuxdude 2013-03-15 08:35:02

+1

即使您可以擁有enum枚舉,因爲程序由兩個不同的機器運行,相同子枚舉的值(例如Type1Cause1)可能會以不同方式實例化。這不危險嗎? – lucasg 2013-03-15 08:38:46

回答

2

正如傑裏所說,這是不可能的。解決這個問題的一種方法是擁有兩個枚舉。一個用於類別,另一個用於子類別。

但是,正如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; 
} 
+0

在這種情況下,我是否需要枚舉類型,因爲它已經在子類型中編碼了? – sajas 2013-03-15 09:00:22

+0

我更新了我的答案,使其實際工作。 – Arne 2013-03-15 09:03:07

+0

但這個設計的問題是,我可以加入錯誤類型與錯誤的子類型,即。我可以將TYPE1與CAUSE4耦合,對吧? – sajas 2013-03-15 09:06:01

7

我甚至不知道什麼枚舉的枚舉意味着什麼。但處理這個 通常的方法是要麼在 定義範圍單一枚舉:

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

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

或者簡單地定義單獨的枚舉,在不同的話,使用 unsigned(或unsigned short,或什麼都)和有點爲 鑄造爲不同的原因。

無論採用的解決辦法,我想將其封裝在 一類,以便客戶端代碼只需要處理errorType()errorCause(); errorCause()甚至可能是 錯誤類型值的模板。 (但某處,每個類型值需要明確的 專業化,因爲編譯器將不會知道如何映射值以導致類型。)

+0

正確的,以某種方式將類型嵌入到原因中是確保不混合錯誤類型和原因的唯一方法。這是做這件事最簡單的方法。 – 2013-03-15 09:50:43

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 
0

我只是無法忍受使用枚舉。所以我有另一個使用顯式類型的答案。 它是不完整的,但顯示了正確的方向,並含有添加的說明等

Here's報關代碼的可能延長:

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

這不是一個完美的解決方案,但顯示瞭如何以更明確的方式處理。有很多改進可以做...