Protobuf repeated子消息编码解码后值恒为0的问题求助
我有一个层级较深的.proto文件,Message为oneof类型,Update是其中一个成员(本文忽略其他成员),Update包含一个repeated类型的Config(为简化仅添加一次),Config存储实际数据(本文用整数示例)。
.proto定义
message Message { message Config { int64 job = 1; int64 job2 = 2; } message Update { repeated Config jobs = 1; } oneof message { Update update = 1; } }
我当前仅尝试完成编码和解码操作(无实际业务逻辑,仅验证功能),过程无报错,但无论job和job2传入什么值,解码后始终返回0。相关代码实现如下:
原C代码实现
#include <stdio.h> #include <pb_encode.h> #include <pb_decode.h> #include "your_proto_file.pb.h" // 替换为实际生成的头文件 void main(void) { /* This is the buffer where we will store our message. */ uint8_t buffer[128]; size_t message_length; bool status; /* Encode our message */ { /* Create a stream that will write to our buffer. */ pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer)); /* Fill in the message */ Message_Config my_job_config_struct = { .job = 345, .job2 = 988, }; /* Configure it as a union, add the encode func for the repeated field encoding */ Message message = { .which_message = Message_update_tag, .my_message.update = { .jobs = { .arg = &my_job_config_struct, .funcs.encode = &encode_Config, }, } }; /* Now we are ready to encode the message! */ status = pb_encode(&ostream, Message_fields, &message); message_length = ostream.bytes_written; /* Then just check for any errors.. */ if (!status) { printf("\nEncoding failed: %s\n", PB_GET_ERROR(&ostream)); return 1; } } printf("~~~~~~~~~~~~~~~~Done Encoding\n"); /* Now we could transmit the message over network, store it in a file or * wrap it to a pigeon's leg. */ /* But because we are lazy, we will just decode it immediately. */ { /* Create a stream that reads from the buffer. */ pb_istream_t istream = pb_istream_from_buffer(buffer, message_length); /* Find out the union type */ const pb_msgdesc_t *type = decode_Update_type(&istream); /* Now we are ready to decode the message. */ if (type == Message_ConfigUpdate_fields) { Message_ConfigUpdate msg = Message_ConfigUpdate_init_zero; msg.jobs.funcs.decode = &decode_Config; status = decode_Update_contents(&istream, Message_ConfigUpdate_fields, &msg); } else { printf("Sad :( no type match\n"); } /* Check for errors... */ if (!status) { printf("Decoding failed: %s\n", PB_GET_ERROR(&istream)); return 1; } } return 0; } bool encode_Config(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { /* Cast the args back to config type */ struct Message_Config *update_submessage = (struct Message_Config *)(*arg); if (!pb_encode_tag_for_field(stream, field)) { return false; } /* Encode the struct as a submessage */ /* If we were repeating this field we'd loop here and encode multiple times */ bool ret = pb_encode_submessage(stream, Message_Config_fields, &update_submessage); return ret; } const pb_msgdesc_t* decode_Update_type(pb_istream_t *stream) { pb_wire_type_t wire_type; uint32_t tag; bool eof; while (pb_decode_tag(stream, &wire_type, &tag, &eof)) { if (wire_type == PB_WT_STRING) { pb_field_iter_t iter; if (pb_field_iter_begin(&iter, Message_fields, NULL) && pb_field_iter_find(&iter, tag)) { /* Found our field. */ return iter.submsg_desc; } } /* Wasn't our field.. */ pb_skip_field(stream, wire_type); } return NULL; } bool decode_Update_contents(pb_istream_t *stream, const pb_msgdesc_t *messagetype, void *dest_struct) { bool status = true; pb_istream_t substream; if (!pb_make_string_substream(stream, &substream)) {return false;} /* messagetype = Message_ConfigUpdate_fields, dest_struct = type Message_ConfigUpdate*/ status &= pb_decode(&substream, messagetype, dest_struct); pb_close_string_substream(stream, &substream); return status; } bool decode_Config(pb_istream_t *stream, const pb_field_t *field, void **arg) { Message_Config config_struct = Message_Config_init_zero; bool ret = pb_decode(&stream, Message_Config_fields, &config_struct); printf("job: %i\n", config_struct.job); printf("job2: %i\n", config_struct.job2); return ret; }
原输出与预期
- 预期输出:
~~~~~~~~~~~~~~~~Done Encoding job: 345 job2: 988 - 实际输出:
~~~~~~~~~~~~~~~~Done Encoding job: 0 job2: 0
问题排查与修复要点
编码阶段参数错误:
encode_Config函数中,pb_encode_submessage的第三个参数错误传入了&update_submessage(二级指针),导致编码了无效的内存地址数据。应直接传入update_submessage(一级指针)。结构体成员名称错误:
生成的Message结构体中,oneof的成员名为message而非my_message,原代码中.my_message.update属于成员名错误,应改为.message.update。解码阶段类型匹配错误:
原代码中使用了不存在的Message_ConfigUpdate_fields,实际应为Message_Update_fields,对应的结构体类型是Message_Update而非Message_ConfigUpdate。解码stream位置未回退:
decode_Update_type函数中,遍历tag时会移动stream的位置,导致后续解码无法读取完整数据。需要在找到目标tag后,用pb_set_position回退到初始位置。子消息解码方式错误:
decode_Update_contents中无需使用pb_make_string_substream,直接调用pb_decode即可解码oneof中的子消息。decode_Config中应使用pb_decode_submessage解码repeated的子消息元素,而非直接调用pb_decode;同时int64类型需用%lld格式化输出,避免类型截断导致的数值错误。
内容的提问来源于stack exchange,提问作者Indigo

