1.创建.proto文件
关于文件规范
指定 proto3 语法
syntax = "proto3";
package 声明符
syntax = "proto3";
package contacts;
定义消息(message)
.proto ⽂件中定义⼀个消息类型的格式为:
message 消息类型名{}// 注意用驼峰命名法
syntax = "proto3";
package contacts;// 定义消息人信息
message PeopleInfo{}
定义消息字段
在 message 中我们可以定义其属性字段,字段定义格式为:字段类型 字段名 = 字段唯⼀编号;
[1] 变⻓编码是指:经过protobuf 编码后,原本4字节或8字节的数可能会被变为其他字节数。
更新 contacts.proto (通讯录 1.0),新增姓名、年龄字段:
syntax = "proto3";
package contacts;// 定义消息人信息
message PeopleInfo{string name = 1;uint32 age = 2;
}
2.编译 contacts.proto ⽂件,⽣成 C++ ⽂件
对以下代码进行编译
syntax = "proto3";
package contacts;// 定义消息人信息
message PeopleInfo{string name = 1;uint32 age = 2;
}
编译命令的格式:
protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto
protoc 是 Protocol Buffer 提供的命令⾏编译⼯具。--proto_path 指定 被编译的.proto⽂件所在⽬录,可多次指定。可简写成 -I IMPORT_PATH 。如不指定该参数,则在当前⽬录进⾏搜索。当某个.proto ⽂件 import 其他
.proto ⽂件时,或需要编译的 .proto ⽂件不在当前⽬录下,这时就要⽤-I来指定搜索⽬录。--cpp_out= 指编译后的⽂件为 C++ ⽂件。
OUT_DIR 编译后⽣成⽂件的⽬标路径。
path/to/file.proto 要编译的.proto⽂件。
更多的信息我们可以通过 protoc --hlep来查看
这里我们执行命令:
protoc --cpp_out=. contacts.proto
其中 . 表示在当前目录中生成。
执行完毕后,
就生成了对应的.h和.cc文件
对于生成的C++代码,包含了以下内容
其中,在.h文件中,可以找到我们之前定义的PeopleInfo的定义
这里可以看到它继承了一个Message类。
再往下翻:
(注意,这里的报错是VS的原因,代码是没有问题的,后续g++编译时可以通过)
这里就可以看到我们当初定义的字段,比如age中有一个 age()方法,这个就是一个类似get的方法,调用后会返回get的值。下面同样还有一个 set_age()方法,同理。
总结:
最重要的序列化和反序列化方法,它放在了在消息类的⽗类MessageLite 中,提供了读写消息实例的⽅法,包括序列化⽅法和反序列化⽅法。
上图中是我们定义的PeopleInfo的父类,它在一个.h文件中
这是它的路径:
这是ProtoBuf给我们实现的。
刚刚也说了,序列化和反序列的方法还放在了消息类的⽗类MessageLite 中,
我们再进去MessageLite 看看
也可以顺便看看它所在的路径
往下翻:
这里就能看到很多关于序列化的方法
大致总结:
class MessageLite {
public://序列化:bool SerializeToOstream(ostream* output) const; // 将序列化后数据写⼊⽂件
流bool SerializeToArray(void *data, int size) const;bool SerializeToString(string* output) const;//反序列化:bool ParseFromIstream(istream* input); // 从流中读取数据,再进⾏反序列化
动作bool ParseFromArray(const void* data, int size);bool ParseFromString(const string& data);
};
3. 序列化和反序列化的使用
代码:
#include <iostream>
#include "contacts.pb.h"using namespace std;int main()
{string people_str; // 将序列化后的二进制数据存在这个string中// 用一个花括号{contacts::PeopleInfo people_info; people_info.set_name("亚索");people_info.set_age(35);if(!people_info.SerializeToString(&people_str)) // 序列化失败就退出{cerr << "序列化error!" << endl;exit(1);}else {cout << "序列化成功: " << people_str << endl;}}{contacts::PeopleInfo people_info; if(!people_info.ParseFromString(people_str)){cerr << "反序列化失败!" << endl;exit(1);}else {cout << "反序列化成功!" << endl;cout << "姓名: " << people_info.name() << endl;cout << "年龄: " << people_info.age() << endl;}}return 0;
}
执行结果:
在打印序列化内容时,由于是二进制的,所以显示内容是不可预期的。
后面反序列化后,打印的结果是符合预期的。
关于编译时:
小结
再回到最初,我们所说的