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

Nanopb技术问询:如何定义包含自嵌套类型的消息?

Nanopb自嵌套消息递归错误解决(保留string字段has_标记)

问题重现

正常非嵌套场景

当消息不包含自嵌套字段时,使用全局max_size配置可以正常生成代码,且会为每个optional string字段生成has_xxx标记:

syntax = "proto2";
import "nanopb.proto";
option (nanopb_fileopt).max_size = 20;
 
message myMessage {
   optional string string1 = 1;
   optional string string2 = 2;
   optional string string3 = 3;
}

生成的C结构体:

typedef struct _myMessage {
    bool has_string1;
    char string1[20];
    bool has_string2;
    char string2[20];
    bool has_string3;
    char string3[20];
/* @@protoc_insertion_point(struct:myMessage) */
} myMessage;

自嵌套场景报错

添加自嵌套字段后,代码生成触发无限递归错误:

syntax = "proto2";
import "nanopb.proto";
option (nanopb_fileopt).max_size = 20;
 
message myMessage {
   optional string string1 = 1;
   optional string string2 = 2;
   optional string string3 = 3;
   optional myMessage submessage = 4;
}

错误信息:

$ python3 generate_c_files.py test/aaa.proto
Today is a good day to code
Traceback (most recent call last):
  File ".../generator/nanopb_generator.py", line 1850, in <module>
    main_cli()
  File ".../generator/nanopb_generator.py", line 1746, in main_cli
    results = process_file(filename, None, options)
  File ".../generator/nanopb_generator.py", line 1704, in process_file
    headerdata = ''.join(f.generate_header(includes, headerbasename, options))
  File ".../generator/nanopb_generator.py", line 1338, in generate_header
    msize = msg.encoded_size(self.dependencies)
  File ".../generator/nanopb_generator.py", line 1068, in encoded_size
    fsize = field.encoded_size(dependencies)
  File ".../generator/nanopb_generator.py", line 670, in encoded_size
    encsize = submsg.encoded_size(dependencies)
  ...
  RecursionError: maximum recursion depth exceeded in __instancecheck__

原因分析

全局(nanopb_fileopt).max_size会触发Nanopb生成器计算整个消息的最大编码大小,而自嵌套结构会导致生成器无限递归计算子消息的大小,最终超出Python递归深度限制。同时,C语言本身不允许结构体直接包含自身实例,这也是后续需要解决的潜在问题。

解决方案

1. 用字段级max_size替代全局配置

将string字段的max_size设置从全局文件级改为字段级,避免触发全局消息大小计算:

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

message myMessage {
   optional string string1 = 1 [(nanopb).max_size = 20];
   optional string string2 = 2 [(nanopb).max_size = 20];
   optional string string3 = 3 [(nanopb).max_size = 20];
   optional myMessage submessage = 4;
}

2. 为自嵌套字段指定指针类型(必做)

C语言不允许结构体直接包含自身,因此需要为自嵌套字段设置指针类型,并可同时指定其最大编码大小避免递归:

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

message myMessage {
   optional string string1 = 1 [(nanopb).max_size = 20];
   optional string string2 = 2 [(nanopb).max_size = 20];
   optional string string3 = 3 [(nanopb).max_size = 20];
   optional myMessage submessage = 4 [(nanopb).type = FT_POINTER, (nanopb).max_size = 100];
}

生成的正确代码示例

修改后生成的C结构体将包含has_xxx标记,且自嵌套字段为指针类型:

typedef struct _myMessage {
    bool has_string1;
    char string1[20];
    bool has_string2;
    char string2[20];
    bool has_string3;
    char string3[20];
    bool has_submessage;
    struct _myMessage *submessage;
/* @@protoc_insertion_point(struct:myMessage) */
} myMessage;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 00:22:18