Using multiple pb_encode_submessage calls for a message with n submessages

14 views
Skip to first unread message

Prince Montano

unread,
Mar 8, 2024, 8:13:04 AMMar 8
to nanopb
Good day, I was wondering about the following.

Suppose that some message samp is defined as follows:

message samp{
    message m1 {
        float d1 = 1;
         ...
    }
    message m2 {
        float d2 = 1;
         ...
    }
   ...
   message mn {
        float dn = 1;
    }
    m1 _m1 = 1;
   m2  _m2 = 2;
    ...
    mn _mn = n;
}

then the encoding routine would be something like:

std::vector<uint8_t> encode_buf{};
Samp samp_msg = samp_init_zero;
samp.funcs.encode = encode_callback;
samp.args = &data;
pb_ostream_t stream = ...
pb_encode(&stream, samp_fields, buf.data());

then the callback should look something like
Type * casted data = (Type*)(*arg);

what is the correct way to decode each submessage manually then adding it to the stream? Is it calling pb_encode_tag_for_field per submessage like so:

for (int i = 0; i < casted_data_size; ++i) {
    if (!pb_encode_tag_for_field(stream, field) { // where field = samp_field
         pb_encode_submessage(stream,  m1_fields, data);
    }
    if (!pb_encode_tag_for_field(stream, field) { // where field = samp_field
         pb_encode_submessage(stream,  m2_fields, data);
    }
    // Do the same routine until we reach submessage mn
}

OR

 calling pb_encode_tag_for_field once and then adding all the submessages in like so?

for (int i = 0; i < casted_data_size; ++i) {
    if (!pb_encode_tag_for_field(stream, field) { // where field = samp_field
         pb_encode_submessage(stream,  m1_fields, data);
         pb_encode_submessage(stream,  m2_fields, data);
         ....
         // Do until mn
    }
}

After looking at multiple results from the internet, it seems that multiple pb_encode_tag_for_field calls followed by pb_encode (the former example) is the way to go. 

Which one of these examples is truly correct and why?

Why do we have to pass the field of the whole main message instead of just the tag for the field of the current submessage? Is this a protobuf type of thing?

Thank you very much for indulging my curiosity. I do understand that using the .options method instead of dealing with submessages especially ones with pb_callback_t types is far more effective and simple, I was just curious about the reason for the implementation for these functions.

Thank you in advance!

Best regards,
Prince



Prince Montano

unread,
Mar 8, 2024, 8:20:25 AMMar 8
to nanopb
Correction: Since I cant edit the post, the ! for each pb_encode_tag_for_field shouldnt be there, I meant to say "we encode our submessage if and only if pb_encode_tag_for_field for the current submessage has succeeded"

Petteri Aimonen

unread,
Mar 8, 2024, 10:19:41 AMMar 8
to nan...@googlegroups.com
Hi,

> After looking at multiple results from the internet, it seems that multiple
> pb_encode_tag_for_field calls followed by pb_encode (the former example) is
> the way to go.

Yes, you need to call pb_encode_tag_for_field() before each submessage.
It's how submessages (including repeated submessages) are encoded in protobuf format.

> Why do we have to pass the field of the whole main message instead of just
> the tag for the field of the current submessage? Is this a protobuf type of
> thing?

pb_encode_tag_for_field() is just a convenience function
https://github.com/nanopb/nanopb/blob/master/pb_encode.c#L680

You can call pb_encode_tag(stream, PB_WT_STRING, field->tag) if you want to.

--
Petteri
Reply all
Reply to author
Forward
0 new messages