如何在C++ Protobuf中使用bytes类型(编码/解码及字段填充)
如何用pb_callback_t填充Protobuf的bytes字段?
我定义了如下Protobuf消息:
syntax = "proto3"; package package; message WriteBlock { uint32 address = 1; uint32 size = 2; bytes data = 3; }
已创建该类型变量并初始化:
package_WriteBlock writeBlock = package_WriteBlock_init_zero;
原本以为Protobuf的bytes类型会被转为string,但生成的代码里对应的字段类型是pb_callback_t:
typedef struct _package_WriteBlock { uint32_t address; uint32_t size; pb_callback_t data; } package_WriteBlock;
pb_callback_t的定义如下:
typedef struct pb_callback_s pb_callback_t; struct pb_callback_s { /* Callback functions receive a pointer to the arg field. * You can access the value of the field as *arg, and modify it if needed. */ union { bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); } funcs; /* Free arg for use by callback */ void *arg; };
请问如何将std::uint8_t byteArray[20]存入data字段?是否需要使用encode()?这些参数的含义是什么?
核心前提
你用的是Nanopb(轻量型Protobuf实现,专为嵌入式/资源受限环境设计),它用pb_callback_t处理动态长度或较大的bytes/string字段,避免预分配固定大小的缓冲区。
实现步骤
要填充data字段,你需要给pb_callback_t绑定一个自定义的编码回调函数,在序列化时自动把byteArray的数据写入输出流。
1. 编写编码回调函数
这个函数负责把你的字节数组写入Nanopb的输出流,参数含义如下:
pb_ostream_t *stream:Nanopb的输出流对象,所有序列化数据都会写入这里(可以是内存缓冲区或文件)const pb_field_t *field:当前处理的字段元数据(比如字段ID、类型,一般不需要修改)void * const *arg:指向你传入的自定义数据指针(这里就是你的byteArray或包含数据+长度的结构体)
示例实现:
#include <pb_encode.h> // 传递字节数组和长度的辅助结构体 typedef struct { const uint8_t* data; size_t length; } ByteArrayData; // 自定义编码回调 bool encode_bytes_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { ByteArrayData* byte_data = (ByteArrayData*)*arg; // 把字节数组写入输出流 if (!pb_write(stream, byte_data->data, byte_data->length)) { return false; // 写入失败返回false,终止序列化 } return true; }
2. 初始化WriteBlock变量和回调
把回调函数绑定到writeBlock.data,并传入你的字节数组数据:
#include <stdint.h> // 你的目标字节数组 uint8_t byteArray[20] = {0x01, 0x02, 0x03, /* ... 其他数据 ... */}; // 初始化辅助结构体 ByteArrayData byte_data = { .data = byteArray, .length = sizeof(byteArray) // 固定长度20 }; // 初始化WriteBlock package_WriteBlock writeBlock = package_WriteBlock_init_zero; writeBlock.address = 0x1234; // 示例地址 writeBlock.size = sizeof(byteArray); // 示例大小 // 绑定回调和数据 writeBlock.data.funcs.encode = encode_bytes_callback; writeBlock.data.arg = &byte_data;
3. 序列化整个消息
调用Nanopb的pb_encode函数,此时会自动触发encode_bytes_callback,把byteArray的数据序列化到输出流中:
uint8_t buffer[128]; // 足够容纳序列化后的消息 pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); // 序列化WriteBlock if (!pb_encode(&stream, package_WriteBlock_fields, &writeBlock)) { // 序列化失败,处理错误 printf("Encode failed: %s\n", PB_GET_ERROR(&stream)); return -1; } // 此时buffer里就是序列化后的Protobuf数据,长度为stream.bytes_written
关键说明
- 为什么用回调?Nanopb默认对
bytes/string字段如果没指定max_size,会生成pb_callback_t,这样可以避免预分配大缓冲区,适合嵌入式场景。 - 如果你的字节数组长度固定且很小,也可以在
.proto文件里给data字段加[nanopb.max_size = 20]选项,重新生成代码后会直接变成固定大小的数组,不需要回调。
内容的提问来源于stack exchange,提问作者peter
相关产品推荐
相关产品推荐

