Nanopb 正确编码和解码子消息中的重复构造字段

Nanopb correctly encoding and decoding repeated construct fields in submessage

encode/decode 重复构造字段的正确方法是什么 Nanopb 子消息?生成的输出显示解码操作确实 不检测任何重复的构造字段。同样耐人寻味的是, encode 回调被调用两次,也有问题。我错过了什么?

如果作为实验,将此示例修改为for,则解码成功 编码和解码不是从 TopMessage 开始,而是从 SubMessage1 开始。还, 在这种情况下,编码回调仅按预期调用一次。

以下是原型定义;有问题的字段是 subMessage11 在 SubMessage1.

syntax = "proto2";
import 'nanopb.proto';

message SubMessage11
{
  required uint64 int64Val = 1;
};

message SubMessage1
{
  repeated SubMessage11 subMessage11 = 1;
};

message SubMessage2
{
  required uint32 intVal = 1;
};

message TopMessage
{
  oneof choice
  {
    SubMessage1 subMessage1 = 1;
    SubMessage2 subMessage2 = 2;
  }
};

使用原型定义的C++代码代码为:

#include "pb_encode.h"
#include "pb_decode.h"

#include "t.pb.h"
#include "debug.hpp"

bool subMessage11EncodeCb(pb_ostream_t *stream, const pb_field_t *field,
    void * const *arg)
{
  dprintf("called, field->tag=%d field->type=%d", field->tag, field->type);

  for(int i=0; i<4; i++)
  {
    if(pb_encode_tag_for_field(stream, field) == false)
    {
      dprintf("encode failed");
      return false;
    }

    SubMessage11 subMessage11 = SubMessage11_init_zero;
    subMessage11.int64Val = 0xaabbccddeef0 + i;

    if(pb_encode_submessage(stream, SubMessage11_fields, &subMessage11) == false)
    {
      dprintf("encode failed");
      return false;
    }
  }

  return true;
}

bool subMessage11DecodeCb(pb_istream_t *stream, const pb_field_t *field,
    void **arg)
{
  dprintf("called");

  SubMessage11 subMessage11 = SubMessage11_init_zero;
  if(pb_decode(stream, SubMessage11_fields, &subMessage11) == false)
  {
    dprintf("error decoding: %s", stream->errmsg);
    return false;
  }

  dprintf("int64Val=%lx", subMessage11.int64Val);

  return true;
}

bool encodeMsg(uint8_t buf[], size_t& bufsz)
{
  dprintf("begin encoding");
  pb_ostream_t stream = pb_ostream_from_buffer(buf, bufsz);

  TopMessage topMessage = TopMessage_init_zero;

  topMessage.which_choice = TopMessage_subMessage1_tag;
  SubMessage1& subMessage1 = topMessage.choice.subMessage1;
  subMessage1.subMessage11.funcs.encode = subMessage11EncodeCb;

  bool status = pb_encode(&stream, TopMessage_fields, &topMessage);
  if(status != true)
  {
    dprintf("error encoding: %s", stream.errmsg);
    bufsz = 0;
    return status;
  }

  bufsz = stream.bytes_written;

  dprintf("done encoding");
  return status;
}

bool decodeMsg(uint8_t buf[], size_t bufsz)
{
  dprintf("begin decoding");
  pb_istream_t stream = pb_istream_from_buffer(buf, bufsz);

  TopMessage topMessage;
  topMessage.which_choice = TopMessage_subMessage1_tag;
  SubMessage1& subMessage1 = topMessage.choice.subMessage1;
  int val;
  subMessage1.subMessage11.arg = (void *)&val;
  subMessage1.subMessage11.funcs.decode = &subMessage11DecodeCb;

  bool status = pb_decode(&stream, TopMessage_fields, &topMessage);
  if(status != true)
  {
    dprintf("error decoding: %s", stream.errmsg);
    return false;
  }

  dprintf("decoded fields: ");

  dprintf("done decoding");
  return status;
}

int main(int ac, char *av[])
{
  uint8_t encBuf[1024];
  size_t encSz = sizeof(encBuf);

  if(encodeMsg(encBuf, encSz) != true)
  {
    dprintf("Encode failed");
    return 1;
  }

  hexdump(encBuf, encSz);

  if(decodeMsg(encBuf, encSz) != true)
  {
    dprintf("Decode failed");
    return 1;
  }
}

产生的输出是:

c.cpp:55:encodeMsg: begin encoding
c.cpp:12:subMessage11EncodeCb: called, field->tag=1 field->type=103
c.cpp:12:subMessage11EncodeCb: called, field->tag=1 field->type=103
c.cpp:74:encodeMsg: done encoding

[0000]   0A 28 0A 08 08 F0 DD F7   E6 BC D7 2A 0A 08 08 F1   ........ ........
[0010]   DD F7 E6 BC D7 2A 0A 08   08 F2 DD F7 E6 BC D7 2A   ........ ........
[0020]   0A 08 08 F3 DD F7 E6 BC   D7 2A                     ........ ..
c.cpp:80:decodeMsg: begin decoding
c.cpp:97:decodeMsg: decoded fields:
c.cpp:99:decodeMsg: done decoding

为子消息多次调用的编码回调是 expected behaviour。第一次调用是计算大小,必须知道大小才能写出子消息体:

If the callback is used in a submessage, it will be called multiple times during a single call to pb_encode. In this case, it must produce the same amount of data every time. If the callback is directly in the main message, it is called only once.

至于为什么你的解码不工作,目前回调are not supported inside oneof constructs:

If a oneof contains a sub-message that has a string field, the encode callback is called twice, and the decode callback is never called.