You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Protobuf repeated子消息编码解码后值恒为0的问题求助

问题: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
    

问题排查与修复要点

  1. 编码阶段参数错误:
    encode_Config函数中,pb_encode_submessage的第三个参数错误传入了&update_submessage(二级指针),导致编码了无效的内存地址数据。应直接传入update_submessage(一级指针)。

  2. 结构体成员名称错误:
    生成的Message结构体中,oneof的成员名为message而非my_message,原代码中.my_message.update属于成员名错误,应改为.message.update。

  3. 解码阶段类型匹配错误:
    原代码中使用了不存在的Message_ConfigUpdate_fields,实际应为Message_Update_fields,对应的结构体类型是Message_Update而非Message_ConfigUpdate。

  4. 解码stream位置未回退:
    decode_Update_type函数中,遍历tag时会移动stream的位置,导致后续解码无法读取完整数据。需要在找到目标tag后,用pb_set_position回退到初始位置。

  5. 子消息解码方式错误:

    • decode_Update_contents中无需使用pb_make_string_substream,直接调用pb_decode即可解码oneof中的子消息。
    • decode_Config中应使用pb_decode_submessage解码repeated的子消息元素,而非直接调用pb_decode;同时int64类型需用%lld格式化输出,避免类型截断导致的数值错误。

内容的提问来源于stack exchange,提问作者Indigo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 16:37:52