protobuf 是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
之前由于要用 levelDB 存 feature,而 levelDB 的 key 只能是 string(? 反正不能是一个数组),所以使用了 protobuf。protobuf 本身还比较 easy,不过目前似乎 protobuf2 仍然是主流,但是由于最近在看 gRPC 的缘故,要使用 protobuf3。如果 protobuf2 没有卸载干净,绝对欲仙欲死……记录一些坑,详细一点的笔记之后补。
// protobuf3 坑好多啊……语法全靠猜,也是有毒
行吧,怪我没找到 orz,生成的 cpp 语法部分在 这里。
先放参考资料好了。一开始找到一个 pdf 文档,说是官方文档的翻译版,但实际上讲得很烂。直接看官方文档比较好。
其中 Language Guide (proto3) 讲了 protobuf3 的 proto 文件的语法相关。
Protocol Buffer Basics: C++ 讲了怎么从编写 proto 文件到在 cpp 中使用的一般步骤(注意此处貌似是按照 protobuf2 讲的)
C++ Generated Code 讲了生成的 cpp 代码的接口,并且强调了 protobuf2 和 protobuf3 的区别。
1syntax = "proto3";
2package test;
3message Feature
4{
5 int32 ver = 1;
6 int32 idx = 2;
7 int32 len = 3;
8 repeated float feat = 4;
9}生成相应代码的语法为:protoc --cpp_out=. test.proto
1#include <iostream>
2#include <cassert>
3#include <cstdlib>
4#include <string>
5#include <vector>
6#include "test.pb.h"
7
8using namespace std;
9
10using namespace test;
11int main()
12{
13
14 float arr[10]={4,5,6,7,8,9};
15 Feature feature;
16 puts("def feature");
17 float ft;
18 for ( int i = 0 ; i < 4 ; i++)
19 {
20 feature.add_feat(arr[i]);
21 //feature.set_feat(3,arr[i]);
22 cout<<"arr[i]:"<<arr[i]<<endl;
23 }
24 for ( int i = 0 ; i < feature.feat_size() ; i++)
25 {
26 float ftval = feature.feat(i);
27 cout<<i+1<<" "<<ftval<<endl;
28 }
29
30
31
32 return 0;
33}编译选项为:g++ mytest.cc test.pb.cc -o test -lprotobuf
可能遇到这个问题
Undefined reference to google protobuf
解决办法是卸载掉系统自带的 protobuf 相关(ubuntu14.04 默认还都是 protobuf2,protobuf3 我是从源码自己编译的)
apt-get remove libprotobuf-dev
如果 X 是单个的变量,那么语法为 set_X(val)
如果 X 是 repeated 类型,语法为 add_X(val)
protobuf 中 message 的嵌套:
对于如下的 .proto
1syntax = "proto3";
2package test;
3message Feature
4{
5 int32 ver = 1;
6 int32 idx = 2;
7 int32 len = 3;
8 repeated float feat = 4;
9 message Single
10 {
11 int64 xxx = 1;
12 string imagePath=2;
13 }
14}使用cpp代码访问如下:
1#include <iostream>
2#include <cassert>
3#include <cstdlib>
4#include <string>
5#include <vector>
6#include "test.pb.h"
7
8using namespace std;
9
10using namespace test;
11int main()
12{
13
14 Feature_Single single;
15 single.set_imagepath("asdzxcxzc");
16 cout<<"imagepath:"<<single.imagepath()<<endl;
17 single.set_xxx(456);
18 cout<<"xxx:"<<single.xxx()<<endl;
19
20 return 0;
21}需要注意,如果我们想要修改某个 class 中的另一个 class 的单个值,需要用 mutable_name() 方法,而不是 name() 方法。参考如下例子:
1syntax = "proto3";
2package test;
3message Feature
4{
5 int32 ver = 1;
6 int32 idx = 2;
7 int32 len = 3;
8 repeated float feat = 4;
9 message Single
10 {
11 int64 QxXx = 1;
12 string imagePath=2;
13 }
14 Single sin = 6;
15}Feature 里嵌套了一个 Single,我现在想修改某个 feature 中的 sin,代码如下:
1#include <iostream>
2#include <cassert>
3#include <cstdlib>
4#include <string>
5#include <vector>
6#include "test.pb.h"
7
8using namespace std;
9
10using namespace test;
11int main()
12{
13 GOOGLE_PROTOBUF_VERIFY_VERSION;
14
15 Feature feature;
16 Feature::Single* haha = feature.mutable_sin();
17
18 haha->set_qxxx(256256);
19 cout<<"val:"<<feature.sin().qxxx()<<endl;
20 return 0;
21}但是如果要修改的是某个 class 中另一个 class 的 repeated 的值,则要使用 add_name。
参考如下例子:
1syntax = "proto3";
2package test;
3message Feature
4{
5 int32 ver = 1;
6 int32 idx = 2;
7 int32 len = 3;
8 repeated float feat = 4;
9 message Single
10 {
11 int64 QxXx = 1;
12 string imagePath=2;
13 }
14 repeated Single sin = 6;
15} 1#include <iostream>
2#include <cassert>
3#include <cstdlib>
4#include <string>
5#include <vector>
6#include "test.pb.h"
7using namespace std;
8using namespace test;
9int main()
10{
11 GOOGLE_PROTOBUF_VERIFY_VERSION;
12
13 Feature feature;
14 Feature::Single *haha = feature.add_sin();
15 haha->set_qxxx(256256);
16 haha = feature.add_sin();
17 haha->set_qxxx(456);
18 cout<<"size:"<<feature.sin_size()<<endl;
19 cout<<"val:"<<feature.sin(0).qxxx()<<endl<<feature.sin(1).qxxx()<<endl;
20 return 0;
21}需要注意的是生成的代码的大小写敏感问题
对于生成的 cpp 代码来说,无论 .proto 文件中的变量名是 Abc 还是 aBc 还是 ABC,在生成的 cpp 代码中都是 abc。
protobuf 的大小写处理有点坑 orz。
troubleshooting#
试图从 3.5 降级到 3.3 时编译 helloworld 无问题,但是运行时报错
1./greeter_client: Symbol `_ZTVN6google8protobuf11MessageLiteE' has different size in shared object, consider re-linking
2./greeter_client: Symbol `_ZTVN6google8protobuf7MessageE' has different size in shared object, consider re-linking
3[libprotobuf FATAL google/protobuf/stubs/common.cc:79] This program was compiled against version 3.3.0 of the Protocol Buffer runtime library, which is not compatible with the installed version (3.5.1). Contact the program author for an update. If you compiled the program yourself, make sure that your headers are from the same version of Protocol Buffers as your link-time library. (Version verification failed in "google/protobuf/any.pb.cc".)
4terminate called after throwing an instance of 'google::protobuf::FatalException'
5 what(): This program was compiled against version 3.3.0 of the Protocol Buffer runtime library, which is not compatible with the installed version (3.5.1). Contact the program author for an update. If you compiled the program yourself, make sure that your headers are from the same version of Protocol Buffers as your link-time library. (Version verification failed in "google/protobuf/any.pb.cc".)折腾了一下午,发现其实并不是 protobuf 的问题。
用 gdb 调试发现,是 grpc 的一个动态库中 check 版本挂掉了。
观察 grpc,发现还是 4 月 20 号左右的版本,于是删掉了所有之前的 grpc 在 /usr/local/lib 下的 4 月的 .so 文件。
再次运行,成功了 orz……
所以这个问题其实是降级 grpc 遇到的问题。
报错太有误导性了。