2015-10-17 306 views
3

在我的代碼庫我有沒有基礎類型類似下面無範圍的枚舉:與protobuf的枚舉替換C++枚舉

enum EFoo { 
    EF_AAA  = 0, 
    EF_UNKNOWN = 1, 
    EF_BBB  = 2, 
    EF_MAX 
} 

我想使它成爲一個protobuf的枚舉,因此它可以在其他protobuf的消息直接作爲被重用枚舉,而不是某種int*字段。所以我想象.proto文件中的枚舉聲明將如下所示:

enum EFoo { 
    EF_AAA = 0; 
    EF_UKNOWN = 1; 
    EF_BBB = 2; 
} 

這裏是一個棘手的部分。隨着時間的推移,可能會添加新的字段,如EF_CCC = 3,所以我不能像聲明EF_MAX那樣聲明C++代碼,因爲它會破壞包含EFoo類型字段的序列化消息的二進制兼容性。並且EF_MAX用於整個代碼庫中的API,因爲類型EFooEF_MAX變量的未知值從未序列化。然而,int類型的EFoo_ARRAYSIZE其語義含義正好是EF_MAX。所以我正在考慮用EFoo_ARRAYSIZE替換所有EF_MAX,但有一件事情讓我困擾,它需要在某些地方做static_cast<EFoo>(EFoo_ARRAYSIZE)以避免編譯器警告,根據標準它將被視爲未定義的行爲,這可能會導致討厭的優化和錯誤。

而我的問題是,我該如何解決我的問題?或者,也許我錯了,我的解決方案與static_cast<EFoo>(EFoo_ARRAYSIZE)全部替換EF_MAX是安全的嗎?

萬一它可能很重要,我在談論C++ 11標準。

回答

0

從協議緩衝器文檔:

在反序列化,無法識別的枚舉值將在消息中被保留,雖然這是如何表示當消息被反序列化是依賴於語言的。在支持指定符號範圍之外的值的開放枚舉類型(如C++和Go)的語言中,未知枚舉值僅作爲其基礎整數表示形式存儲。

在C++中的int可強制轉換爲枚舉,如果你知道的INT表示有效枚舉。這不是未定義的行爲,它是該語言的一個可預測的特徵。

請注意,由protobuf生成的枚舉(至少今天)不是enum class類型,僅僅是enum

從這裏:https://developers.google.com/protocol-buffers/docs/proto3#enum

通過研究protoc的輸出。

+0

「從協議緩衝區文檔」 - 鏈接? –

+0

真的嗎?我需要爲您的谷歌「協議緩衝區文件」?確定這裏是:https://developers.google.com/protocol-buffers/docs/proto3#enum –

+0

我認爲適當添加一個特定的引用盡可能直接引用,特別是如果它可以幫助讀者知道規範的來源,以及未來的發展方向。特別是如果有人剛剛瞭解這個問題(畢竟這是很多人的想法)。考慮將其添加到您的帖子。 –

2

Proto3強制所有枚舉通過添加兩個「定點」值與INT_MIN和INT_MAX值枚舉爲32位:

enum Foo { 
    Foo_FOO = 0, 
    Foo_BAR = 1, 
    Foo_Foo_INT_MIN_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32min, 
    Foo_Foo_INT_MAX_SENTINEL_DO_NOT_USE_ = ::google::protobuf::kint32max 
}; 

這樣做的效果是,枚舉類型Foo將始終使用一個32位整數(或更大)作爲其表​​示,因此您可以使用static_cast s將任何32位值存儲到其中。

請注意,proto2沒有這樣做,因此將ARRAYSIZE常量作爲proto2中的枚舉類型進行強制轉換並不安全。特別是,在proto2中,如果您有一個枚舉的最大值爲127,則可能使用8位有符號類型來表示。 ARRAYSIZE值將是128,這將超出該類型的範圍 - 它可能被視爲-128。或者更糟糕的是,編譯器可能會應用優化,導致完全不可預知的行爲。

這就是爲什麼proto2處理像未知字段這樣的未知字段上的未知枚舉值的原因,其行爲像字段值甚至不存在。這種行爲混淆了很多人,因此proto3切換到強制枚舉到32位。 (請注意,proto3的方法意味着如果你在枚舉值上有一個switch()語句,它必須有一個默認情況,否則你會得到一個警告。)

+0

出於好奇,在proto2中,出於安全原因,將電報上的未知枚舉值視爲未知字段? –

+1

@KostyaBazhanov有一個可能的安全性參數:收件人可能包含switch()語句,該語句涵蓋每個預期的情況,但缺少默認情況。程序員不太可能想到在出現意外值的情況下會發生什麼情況,並且可能最終導致可利用的錯誤。通過將該值視爲未知字段,字段的getter現在返回該字段的默認值,這至少是程序員可能已經考慮和處理的情況。 OTOH,proto3迫使你有一個默認情況;也許這很好。 –