使用Protobuf在C++17中序列化时出现段错误:消息作为字段引发的问题
Protobuf在C++17中序列化时出现段错误:消息作为字段引发的问题
之前我碰到过一个类似的Protobuf序列化报错问题,但这次我把问题精准定位后,还是决定单独来梳理下。我的需求很明确:用Protobuf在C++17环境下完成数据序列化,但过程中触发了段错误,而且我已经把问题范围缩小到消息作为字段这个场景上了。
我在./proto目录下的proto文件内容大概是这样的:
syntax = "proto3"; package my_proto; // 定义内部消息 message DetailInfo { int32 code = 1; string content = 2; } // 外部消息,把内部消息作为字段 message MainData { DetailInfo detail = 1; repeated string tags = 2; }
最开始我写的C++代码大概是这样的,结果一运行就段错误:
#include <iostream> #include "proto/main_data.pb.h" int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; MainData data; // 错误写法:直接访问detail字段的成员 data.detail().set_code(404); data.detail().set_content("not found"); std::string out_str; data.SerializeToString(&out_str); // 这里触发段错误 google::protobuf::ShutdownProtobufLibrary(); return 0; }
后来我折腾了好一阵子才搞明白问题出在哪!原来Protobuf的嵌套消息字段,默认是处于未初始化的状态,直接通过data.detail()获取的是const引用,根本不能用来修改成员,而且底层对象可能还没分配内存,直接访问自然就踩内存了。
正确的做法应该是用Protobuf提供的mutable_<字段名>()方法,先获取可修改的消息指针,确保对象被正确初始化:
#include <iostream> #include "proto/main_data.pb.h" int main() { GOOGLE_PROTOBUF_VERIFY_VERSION; MainData data; // 正确:用mutable_detail()获取可修改的指针 data.mutable_detail()->set_code(200); data.mutable_detail()->set_content("success"); // 如果是repeated类型的消息字段,要用add_<字段名>()来添加新元素 data.add_tags("protobuf"); data.add_tags("c++17"); std::string out_str; if (data.SerializeToString(&out_str)) { std::cout << "序列化成功,长度:" << out_str.size() << std::endl; } else { std::cerr << "序列化失败!" << std::endl; } google::protobuf::ShutdownProtobufLibrary(); return 0; }
另外还要注意,如果是用set_allocated_<字段名>()手动分配内存的情况,一定要记得不要重复释放,Protobuf在销毁外层消息时会自动释放内部分配的消息对象,所以手动分配后别自己再delete了,不然会触发双重释放的错误。
总结下来,这种段错误本质上就是没搞懂Protobuf C++接口的内存管理逻辑,误用了const引用或者访问了未初始化的嵌套消息对象,只要跟着官方接口的规范来,用对mutable系列方法,基本就能避开这个坑。
备注:内容来源于stack exchange,提问作者Daniil Yefimov
相关产品推荐
相关产品推荐

