【问题标题】:Nanopb correctly encoding and decoding repeated construct fields in submessageNanopb 正确编码和解码子消息中的重复构造字段
【发布时间】:2016-10-04 14:05:30
【问题描述】:

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

如果作为实验,将此示例修改为 编码和解码不是从 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;
  }
};

使用 proto 定义的 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

【问题讨论】:

  • 只是好奇,TopMessage_fields 到底是在哪里定义的?
  • 啊,现在我明白了,它会在t.pb.h自动生成

标签: nanopb


【解决方案1】:

多次调用子消息的编码回调是expected behaviour。第一个调用是计算大小,在写出子消息正文之前必须知道它:

如果在子消息中使用回调,它将在一次调用 pb_encode 期间被多次调用。在这种情况下,它必须每次产生相同数量的数据。如果回调直接在主消息中,则只调用一次。

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

如果 oneof 包含具有字符串字段的子消息,则编码回调被调用两次,而解码回调永远不会被调用。

【讨论】:

  • 这有助于至少知道目前在 oneof 结构中不支持回调,并且至少可以解决部分问题。但是,在编码方面,当有 4 个或更多重复元素要编码时,我只看到两个回调。在那种情况下我错过了什么?
  • 看起来 4 个元素的循环在您的回调中,并且子消息中没有进一步的回调。所以回调将在一次调用中处理所有 4 个元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-09
  • 1970-01-01
  • 2015-12-07
  • 2019-06-04
  • 1970-01-01
  • 2016-04-04
  • 1970-01-01
相关资源
最近更新 更多