c++ - ProtoBuf:如何为新消息重用 FieldDescriptor?如何有效地从动态消息中获取字段值?
问题描述
我目前正在从事一个项目,我想在其中使用 Google 协议缓冲区 (C++) 来序列化和反序列化数据。该应用程序具有以下要求:
- 一项要求是不使用从文件生成预编译 C++ 类的标准方法
.proto
(使用 protoc)。相反,我使用. 在运行时google::protobuf::compiler::Importer
导入我的.proto
文件,然后google::protobuf::Message
使用google::protobuf::DynamicMessageFactory
. 使用这种方法,我已经可以序列化/反序列化字节数组/消息,而无需事先生成 C++ 类。 - 性能:我预计大约每 50 毫秒就有一次新的字节流,因此解析字节数组并从动态创建的消息中读取值必须非常有效。对于解析步骤,我只是使用标准
ParseFromArray(…)
方法来获取我的消息。目前,我认为不需要优化这一步。相反,我目前正在寻找一种更有效地检索值的方法。
我知道 1. 和 2. 有点矛盾,因为使用DynamicMessageFactory
可能比生成预编译的 C++ 类更昂贵,但不幸的是 1. 是硬性要求并且无法更改。
要检索我当前正在使用的已解析消息的值google::protobuf::Reflection
并google::protobuf::Descriptor
遍历我的消息,直到google::protobuf::FieldDescriptor
找到对应的消息。由于遍历消息的效率非常低,尤其是当一条消息预计有大约 1000 个字段时,我想到了FieldDescriptor
在地图中缓存找到的内容,然后将缓存FieldDescriptor
的 's 用于其他消息,而无需再次遍历每条消息(因为我所有的消息具有相同的结构)。这种方法是在这里提出的。不幸的是,我没能让它工作(见下面我的简化示例代码)。你们能帮帮我吗?提前感谢您的任何建议。
int main()
{
MyUtil util; // My protobuf utility class
int size;
uint8_t* data = util.GenerateByteArray(&size); // Generate sample byte array
Message* message1 = util.ParseFromArray(data, size);
util.SetDouble(message1, "position.x", 1.111);
Message* message2 = util.ParseFromArray(data, size);
util.SetDouble(message2, "position.x", 2.222);
cout << util.GetDouble(message1, "position.x") << endl;
cout << util.GetDouble(message2, "position.x") << endl;
// This works as expected if my GetDouble method iterates through the whole message for every new message.
// But if I try to cache my FieldDescriptors I get the wrong output (see below).
return 0;
}
class MyUtil
{
private:
// The cached descriptors
const Message* mTempMessage = nullptr;
const FieldDescriptor* mTempField = nullptr;
public:
MyUtil() { /*Initializing and importing .proto files*/ }
// ... Some other methods
double GetDouble(const Message* message, const string& path)
{
if (mTempField)
{
// Problem: This doesn't work if I only pass the actual 'message2' so I attempted to also cache mTempMessage.
// But mTempMessage will always refer to the original message1 from which mTempField was retrieved.
// This is why util.GetDouble(message2, "position.x") will always only return the value of message1.
// So I know why my output is wrong but I don't know how the correct solution should look like.
return mTempMessage->GetReflection()->GetDouble(*mTempMessage, mTempField);
}
vector<string> fieldNames;
boost::split(fieldNames, path, boost::is_any_of(".")); // "position.x" => ["position", "x"]
vector<string>::iterator iterator = fieldNames.begin();
vector<string>::iterator last = fieldNames.end() - 1;
return GetDouble(message, &iterator, &last);
}
double GetDouble(const Message* message, vector<string>::iterator* pathIterator, vector<string>::iterator* pathLast)
{
// Get FieldDescriptor using message->GetDescriptor()->FindFieldByName(...)
const FieldDescriptor* field = GetField(message, **pathIterator);
if (*pathIterator != *pathLast)
{
// Field "position": Is another message, so call recursively for next field
(*pathIterator)++;
// Get the inner "position" message using Reflection
const Message* messageField = GetMessageField(message, field);
return GetDouble(messageField, pathIterator, pathLast);
}
else if (field->type() == FieldDescriptor::TYPE_DOUBLE)
{
// Field "x": Actual value can be retrieved
// Caching the found FieldDescriptor (and also the current "position" message)
mTempMessage = message;
mTempField = field;
return message->GetReflection()->GetDouble(*message, field);
}
else
{
// Return some invalid value
}
}
};
解决方案
推荐阅读
- java - 如何通过 2 个浮点属性对 ArrayList 中的对象进行排序?
- python-3.x - Tkinter 专注于弹出窗口
- gradle - 运行 Gradle.run() 任务工作正常,来自 Idea "UnsatisfiedLinkError: Failed to locate library: lwjgl.dll"
- c# - xpaths 的正确和最佳实践?
- r - 以编程方式获取有关在 R 中绘制输出的下落的信息
- c# - 如何将 XML 编码为 base64,然后将其保存到流中?
- angularjs - ngTable 中的延迟加载数据
- mongoexport - 来自远程服务器的 MongoExport
- laravel - 如何在不将 server.php 重命名为 index.php 的情况下从 laravel6 url 中删除 public
- android - 是否可以在 WorkManager 的 doWork() 中创建 Main Activity?