4

I have the need to define proto fields of a message on runtime and send data along with them. So what I've done is I've defined them on runtime using FieldDescriptorProto and send it from server to client.

My proto file looks like this:

syntax = "proto3";

import "google/protobuf/descriptor.proto";

package handshake;

option cc_enable_arenas = true;

message ProtoRequest {
    google.protobuf.FileDescriptorProto custom_proto = 1;
}

service HandShake {    
    rpc sendProto (ProtoRequest) returns (ProtoResponse) {}
}

message ProtoResponse {
    bool received = 1;
}

what I did is I defined the fields and set values to them in the server-side, something like this:

// --- server ---

// assume all fields are int64
const google::protobuf::Message* message = factory.GetPrototype(message_desc);
google::protobuf::Message* mutable_msg = message->New(&arena);

const Reflection* reflection = mutable_msg->GetReflection();
const Descriptor* descriptor = mutable_msg->GetDescriptor();

for (int i = 0; i < descriptor->field_count(); i++) {
    const FieldDescriptor * fd = descriptor->field(i);
    reflection->SetInt64(mutable_msg,fd,i);
}

// verification
for (int i = 0; i < descriptor->field_count(); i++) {
    const FieldDescriptor* fd = descriptor->field(i);
    // reflection->SetInt64(mutable_msg, fd, i);
    cout << fd->name() << " -> " << reflection->GetInt64(*mutable_msg, fd) << endl;
}

I verify if the values are set on the server-side and it was good. But When I transfer the FileDescriptor to the client:

// --- client ---

FileDescriptorProto local_proto = request->custom_proto(); //this is from the server

//const google::protobuf::Descriptor* message_desc = local_proto.GetDescriptor();

DescriptorPool pool_;
const FileDescriptor* local_proto_;
local_proto_ = pool_.BuildFile(local_proto);

// display message contents
const google::protobuf::Descriptor* message_desc = local_proto_->FindMessageTypeByName("AttribRow");
cout << "Field Count: " << message_desc->field_count() << endl;

google::protobuf::DynamicMessageFactory factory;
const google::protobuf::Message* mutable_msg = factory.GetPrototype(message_desc);

const Reflection* reflection = mutable_msg->GetReflection();
const Descriptor* descriptor = mutable_msg->GetDescriptor();

for (int i = 0; i < descriptor->field_count(); i++) {
    const FieldDescriptor* fd = descriptor->field(i);
    cout << fd->name() << " -> " << reflection->GetInt64(*mutable_msg, fd) << endl; //this fails
}

When I verify if I received the data successfully on the client side, all the fields are set to 0 (default value of the datatype).

So what I need in simple words is, I want to transfer data as protobuf, whose field names I know only at runtime. So I should be defining proto fields for those and transfer data along with them from server to client successfully.

Azeem
  • 11,148
  • 4
  • 27
  • 40
  • 2
    First thing I would do is look at the actual protobuf sent (and received) and make sure your fields w/values are in there. (Remembering of course that the protobuf has no field names, only field numbers.) To be more precise: Look at the actual protobuf to see what's in there at all (maybe the field #s aren't what you think). Interesting question - I had no idea this capability existed. – davidbak Jun 29 '20 at 14:01
  • What is the value of `descriptor->field_count()` on client-side? – Azeem Jun 29 '20 at 14:09
  • 1
    @Azeem 114 and it is exact . I have no problems with that – Deepak Gurumoorthy Jun 30 '20 at 04:44
  • @DeepakGurumoorthy: And, the serialized message is of the same size as well (in bytes at sending and receiving ends)? – Azeem Jun 30 '20 at 04:48
  • @DeepakGurumoorthy I suspect the issue has to do with how the proto is being serialized and deserialized. For debugging purposes, I would suggest trying to locally serialize, deserialize and then verify if the contents are the same. – Yash Tibrewal Jul 07 '20 at 04:31

0 Answers0