mardi 7 mars 2023

Using protobuf reflection to check for default values

When am iterating over a a protobuf message fields using reflection, I'd like to check if a sub-message or a map's value has a default value. I am currently checking for ByteSizeLong() == 0, which works, but according to the documentation is linear in the number of sub-fields, making it inefficient for large sub-messages.

I tried to use HasField() instead, but it isn't working.

Is there a problem in my code or can HasField() not be used for this? And if the latter is the case, is there an O(1) function that can be used?

Here's a minimal example:

The .proto file:

message Wrapper {
  string value = 1;
}

message M {
  map<int64, Wrapper> map = 1;
  optional Wrapper    msg = 2;
}

And the C++ code:

int main() {
    M msg;
    auto status = JsonStringToMessage(R"(
        {
          "map": {
            "1": { "value": "string1" },
            "2": {}
          },
          "msg": {}
        })",
        &msg);

    test(msg);
}

void test(const Message& msg) {
    const auto* descr = msg.GetDescriptor();
    const auto* refl = msg.GetReflection();
    for (int i = 0; i < descr->field_count(); ++i) {
        const auto* fd = descr->field(i);
        if (fd->is_map()) {
            for (const auto& element : refl->GetRepeatedFieldRef<Message>(msg, fd)) {
                const auto* descr = element.GetDescriptor();
                const auto* refl = element.GetReflection();
                const auto* fdValue = descr->map_value();
                std::cout << "Element size: " << refl->GetMessage(element, fdValue).ByteSizeLong()
                          << ", has field: " << refl->HasField(element, fdValue) << "\n";
            }
            continue;
        }

        if (fd->type() == FieldDescriptor::TYPE_MESSAGE && !fd->is_repeated()) {
            std::cout << "Message size: " << refl->GetMessage(msg, fd).ByteSizeLong()
                      << ", has field: " << refl->HasField(msg, fd) << "\n";
        }
    }
}

And finally, the results:

Element size: 9, has field: 1
Element size: 0, has field: 1
Message size: 0, has field: 1




Aucun commentaire:

Enregistrer un commentaire