用protobuf枚举替换C ++枚举

时间:2015-10-17 10:50:07

标签: c++ c++11 enums refactoring protocol-buffers

在我的代码库中,我有一个没有底层类型的无范围枚举,如下所示:

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中使用,因为类型EFooEF_MAX的变量的未知值从未被序列化。但是,EFoo_ARRAYSIZE类型为int,其语义正好为EF_MAX。所以我正在考虑用EF_MAX替换所有EFoo_ARRAYSIZE,但有一件事困扰我,它需要在某些地方执行static_cast<EFoo>(EFoo_ARRAYSIZE)以避免编译器警告并根据标准它将被视为未定义的行为,这可能导致讨厌的优化和错误。

我的问题是,我该如何解决我的问题?或者我错了,我用EF_MAX替换所有static_cast<EFoo>(EFoo_ARRAYSIZE)的解决方案是否安全?

如果它可能很重要,我说的是C ++ 11标准。

2 个答案:

答案 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的输出。