在我的代码库中,我有一个没有底层类型的无范围枚举,如下所示:
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
,因此我不能像在C ++代码中那样声明EF_MAX
,因为它会破坏与包含EFoo
类型字段的序列化消息的二进制兼容性。并且EF_MAX
在整个代码库中的API中使用,因为类型EFoo
和EF_MAX
的变量的未知值从未被序列化。但是,EFoo_ARRAYSIZE
类型为int
,其语义正好为EF_MAX
。所以我正在考虑用EF_MAX
替换所有EFoo_ARRAYSIZE
,但有一件事困扰我,它需要在某些地方执行static_cast<EFoo>(EFoo_ARRAYSIZE)
以避免编译器警告并根据标准它将被视为未定义的行为,这可能导致讨厌的优化和错误。
我的问题是,我该如何解决我的问题?或者我错了,我用EF_MAX
替换所有static_cast<EFoo>(EFoo_ARRAYSIZE)
的解决方案是否安全?
如果它可能很重要,我说的是C ++ 11标准。
答案 0 :(得分:2)
Proto3通过添加两个&#34; sentinel&#34;强制所有枚举为32位。具有INT_MIN和INT_MAX值的枚举值:
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
将任何32位值存储到其中第
请注意,proto2没有这样做,因此将ARRAYSIZE常量转换为proto2中的枚举类型是不安全的。特别是在proto2中,如果你有一个最大值为127的枚举,它可能用8位有符号类型表示。 ARRAYSIZE值为128,超出该类型的范围 - 可能被视为-128。或者更糟糕的是,编译器可能会应用导致完全不可预测的行为的优化。
这就是为什么proto2在线上处理未知的枚举值,就像未知的字段一样,表现得像字段值一样甚至不存在。这种行为使很多人感到困惑,因此proto3切换到强制枚举为32位。 (请注意,proto3的方法意味着如果你对枚举值有一个switch()语句,它必须有一个默认情况,否则你会收到警告。)
答案 1 :(得分:0)
来自协议缓冲区文档:
在反序列化期间,无法识别的枚举值将保留在消息中,但是当反序列化消息时,如何表示这种值取决于语言。在支持具有超出指定符号范围的值的开放枚举类型的语言中,例如C ++和Go,未知的枚举值只是作为其基础整数表示存储。
在c ++中,如果知道 int表示有效的枚举,则可以将int强制转换为枚举。这不是未定义的行为,它是该语言的可预测特征。
请注意,protobuf(至少今天)生成的枚举不是from PIL import Image
import requests
import numpy as np
from StringIO import StringIO
response = requests.get(url)
img = np.array(Image.open(StringIO(response.content)))
类型,而只是enum class
。
从这里开始:https://developers.google.com/protocol-buffers/docs/proto3#enum
通过研究protoc的输出。