我使用协议缓冲区的反射功能在运行时读取消息字段值。
原型我:
package xapp.battle;
message BATTLE_DATA {
repeated AInfo aInfo = 1;
repeated BInfo bInfo = 2;
repeated CInfo cInfo = 3;
// a lot other repeated messages
}
message AInfo {
int32 test_field = 1;
// ......
}
message BInfo {
int32 test_field = 1;
// ......
}
message CInfo {
int32 test_field = 1;
// ......
}
我现在的代码:
void DO_SOMETHING(messageName) {
const Descriptor* pDescriptor = BATTLE_DATA->GetDescriptor();
const FieldDescriptor* pMessageField = pDescriptor->FindFieldByName(messageName);
const Reflection* pReflection = BATTLE_DATA->GetReflection();
if (messageName == "aInfo") {
const RepeatedPtrField<::xapp::battle::AInfo> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::AInfo>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::AInfo messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
}
else if (messageName == "bInfo") {
const RepeatedPtrField<::xapp::battle::BInfo> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::BInfo>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::BInfo messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
}
else if (messageName == "CInfo") {
const RepeatedPtrField<::xapp::battle::CInfo> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::CInfo>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::CInfo messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
}
// ......
else {
LOG("loadOneBin - Unknown messageName");
}
}
此代码有效,但显然它不是最好的解决方案,因为有太多的重复&#34; else-if&#34;代码块。
我想要的是(至少摆脱那些&#34;否则 - 如果&#34;块):
const RepeatedPtrField<::xapp::battle::MESSAGE_NAME> repeated_ptr_field = pReflection->GetRepeatedPtrField<::xapp::battle::MESSAGE_NAME>(*BATTLE_DATA, pMessageField);
for (int i = 0; i < repeated_ptr_field.size(); i ++) {
::xapp::battle::MESSAGE_NAME messageInfo = repeated_ptr_field.Get(i);
// continue to read the test_field value of messageInfo
}
GetRepeatedPtrField的源代码:
template<typename PB>
inline const RepeatedPtrField<PB>& Reflection::GetRepeatedPtrField(
const Message& message, const FieldDescriptor* field) const {
return *static_cast<RepeatedPtrField<PB>* >(
MutableRawRepeatedField(const_cast<Message*>(&message), field,
FieldDescriptor::CPPTYPE_MESSAGE, -1,
PB::default_instance().GetDescriptor()));
}
任何建议都将不胜感激,谢谢:)
答案 0 :(得分:2)
正确答案
这里的正确答案是,您应该使用Reflection::GetRepeatedPtrField<T>()
为重复字段的每个元素获取通用Reflection::GetRepeatedMessage()
,而不是使用const Message&
。不幸的是,您需要为每个元素调用此方法一次(使用Reflection::FieldSize()
来查找大小)。
在每个Message
上,您可以使用Message::GetDescriptor()
,查找名为test_field
的字段,然后查找Message::GetReflection()
并使用该字段来读取该字段的值。作为优化,您可以安全地假设同一重复字段中的所有邮件都具有相同的Descriptor
和Reflection
个对象,因此您只需要获取这些对象并查找FieldDescriptor
一次对于整个阵列。
像(未经测试)的东西:
int size = pReflection->FieldSize(*BATTLE_DATA, pMessageField);
Reflection* pInnerReflection = NULL;
FieldDescriptor* pTestFieldDesc = NULL;
for (int i = 0; i < size; i++) {
const Message& msg = pReflection->GetRepeatedMessage(
*BATTLE_DATA, pMessageField, i);
if (pInnerReflection == NULL) {
pInnerReflection = msg.GetReflection();
pTestFieldDesc = msg.GetDescriptor()
->FindFieldByName("test_field");
}
int testField = pInnerReflection->GetInt32(msg, pTestFieldDesc);
// ...
}
一个有趣但不好的答案
这第二个解决方案会稍微好一点,看起来更漂亮一点。不幸的是,它是技术未定义的行为。它将在所有编译器上实际工作,但除非遇到性能问题,否则您不应该这样做。我把它包括在这里是为了好玩,因为无论如何你都可能想出来,所以最好知道它为什么不好。
您可以致电GetRepeatedPtrField<google::protobuf::Message>(...)
以获取RepeatedPtrField
个通用Message
个对象。然后,您可以像第一个解决方案一样获取描述符和反射,但不再需要调用Reflection::GetMessage
(虚拟调用)来读取每条消息。 (但你仍然需要调用反射来阅读test_field
。)
这是技术上未定义的行为,因为它假定了一些事情:
RepeatedPtrField<T>
所有T
的布局相同。这实际上是正确的,因为RepeatedPtrField<T>
扩展RepeatedPtrFieldBase
并且不添加任何新字段,但C ++标准并不要求这种情况。AInfo*
)向上转换为Message*
不会更改指针的位。实际上,只要没有多重继承,并且Protobufs不使用多重继承(Google C ++样式指南甚至禁止它),所有编译器都是如此。RepeatedPtrField<Message>
的访问权限,而不是对RepeatedPtrField<AInfo>
(或其中任何类型)的其他访问权限。程序。在实践中,想象任何编译器实际为此用例执行此操作是不合理的,但在技术上允许C ++。