В настоящее время я работаю над проектом, в котором я хочу использовать буфер протокола Google (C ++) для сериализации и десериализации данных. Приложение имеет следующие требования:
- Одно из требований - НЕ использовать стандартный подход генерации предварительно скомпилированных классов C ++ из файлов
.proto
(используя proto c). Вместо этого я использую 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
для других сообщений, не просматривая каждое из них. сообщение снова (так как все мои сообщения имеют одинаковую структуру в любом случае). Этот подход был предложен здесь . К сожалению, мне не удалось заставить его работать (см. Мой упрощенный пример кода ниже). Ребята, вы можете мне помочь? Заранее благодарю за любые предложения.
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
}
}
};