前言
本文只介绍如何安装protobuf
, 如何编译使用了protobuf
的 C++ 代码
至于详细的protobuf
对应产生的 C++ 的 API, 下一篇博客再讨论
安装并配置环境变量
- 安装辅助工具
sudo apt-get install autoconf automake libtool curl make g++ unzip
- 获取源代码,在发布页面中下载一个
.tar.gz
或.zip
软件包 - 解压至指定目录
tar -xzf protobuf-all-3.7.0.tar.gz -C ~/Protocol_Buffer
- 执行以下命令安装
(默认是安装在 /usr/local,也可以通过添加--prefix==PATH参数指定安装目录)
$./configure --prefix=/usr/local/protobuf
$ make
$ make check
$ make install
以上命令需要sudo
权限, 并且可能需要一段时间, 耐心等待
- 在
/etc/profile
中添加 (这部分内容摘自博客)
#(动态库搜索路径) 程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/
#(静态库搜索路径) 程序编译期间查找动态链接库时指定查找共享库的路径
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/protobuf/lib/
#执行程序搜索路径
export PATH=$PATH:/usr/local/protobuf/bin/
#c程序头文件搜索路径
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/protobuf/include/
#c++程序头文件搜索路径
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/protobuf/include/
#pkg-config 路径
export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/
- reboot, 使用
echo $LIBRARY_PATH
验证是否成功
简单例子
- 创建
address.proto
文件
syntax = "proto3"; //指明使用proto3语法
package tutorial; //包声明符, 产生的类会被包装在C++命名空间中
message Persion //声明消息
{
string name = 1;
int32 age = 2;
}
message AddressBook //AddressBook消息中包含一个Persion消息
{
Persion persion = 1;
}
消息中每个字段后"= 1", “= 2"标记标识该字段在二进制编码中使用的唯一"标记”
protobuf
中message
是一些列键值对, message
的二进制版本只是使用字段号作为key
, 每个字段的名称和声明类型只能在解码端通过引用消息类型的定义(即.proto
文件)来确定
- 编译
address.proto
文件
protobuf 编译器 protoc
, 查看版本使用
$ protoc -- version
编译文件 : protoc 待编译文件 --cpp_out=输出目录
, –cpp_out指的是输出为 C++ 版本
$ protoc address.proto --cpp_out=.
编译成功后, 在你制定的输出目录下生成address.pb.h
和address.pb.cc
两个文件
- 创建
useProtoBuf.cpp
文件, 在 c++ 代码中使用我们以protobuf
协议编写的消息
#include "address.pb.h"
void func(tutorial::Persion* ptr)
{}
int main()
{
tutorial::AddressBook addressBook_;
}
- 编译
$ g++ useProtoBuf.cc address.pb.cc -lprotobuf
在C++中使用
本篇博客讨论使用protobuf
定义的消息 message, 使用 protoc 编译生成对应的.h
和 .cc
文件后, 其中对应产生的API
定义一个.proto文件 : msg.proto
syntax = "proto3";
package test_2;
message usr_login
{
string usrname = 1;
string usrpasswd = 2;
}
message usr_info
{
int32 classrome = 1;
string studentID = 2;
}
message student
{
int32 STAMP = 1;
usr_login usrLogin = 2;
usr_info usrInfo = 3;
repeated int32 array = 4;
}
使用方法
#include <assert.h>
#include <iostream>
#include "msg.pb.h"
using namespace std;
int main()
{
test_2::student student_;
cout << "类所占字节数 = "
<< student_.ByteSize()
<< endl;
//对于int类型的只提供获取,修改和清楚三个API
student_.set_stamp(123); //生成的API都是小写
//消息中的自定义类型,并没有set借口,而是要通过mutable_*接口
//来得到对应指针进而去进行操作
auto usrLoginPtr = student_.mutable_usrlogin();
auto usrInfoPtr = student_.mutable_usrinfo();
string name("lzj");
string passwd("123456");
usrLoginPtr->set_usrname(name);
usrLoginPtr->set_usrpasswd(passwd);
string id("04161027");
usrInfoPtr->set_classrome(5);
usrInfoPtr->set_studentid(id);
cout << "未初始化前array长度 = "
<< student_.arr_size()
<< endl;
for (int i = 0; i < 5; i++)
{
//对于repeated的类型,add_*()接口增加一条记录
//该接口返回一个指向该条记录的指针,用来做相关操作
//我这里只是简单的里面一个int
auto arrPtr = student_.add_arr();
arrPtr->set_id(i);
}
//将其转化为字符串
//即可用在网络编程通信时
string buff;
//序列化为字符串
bool isSucceed = student_.SerializePartialToString(&buff);
assert(isSucceed == true);
/*********parsing***************/
test_2::student res;
//反序列化
isSucceed = res.ParseFromString(buff);
assert(isSucceed == true);
cout << "student`s STAMP = "
<< res.stamp()
<< endl;
usrLoginPtr = res.mutable_usrlogin();
cout << "usrLogin`s usrname = "
<< usrLoginPtr->usrname()
<< endl;
cout << "usrLogin`s usrpasswd = "
<< usrLoginPtr->usrpasswd()
<< endl;
usrInfoPtr = res.mutable_usrinfo();
cout << "usrInfo`s classrome = "
<< usrInfoPtr->classrome()
<< endl;
cout << "usrInfo`s studentID = "
<< usrInfoPtr->studentid()
<< endl;
//遍历数组
for (int i = 0; i < res.arr_size(); i++)
{
//获得一个该下标的元素的引用
//返回值为const类型的,不能通过该返回值去进行修改
auto tmpPtr = res.arr(i);
cout << "arr["
<< i
<< "] = "
<< tmpPtr.id()
<< endl;
}
cout << endl;
//返回下标为2的数组元素的指针
auto tmpArr = res.mutable_arr(2);
//改变其值
tmpArr->set_id(99);
for (int i = 0; i < res.arr_size(); i++)
{
auto tmpPtr = res.arr(i);
cout << "arr["
<< i
<< "] = "
<< tmpPtr.id()
<< endl;
}
}
输出结果
类所占字节数 = 0
未初始化前array长度 = 0
student`s STAMP = 123
usrLogin`s usrname = lzj
usrLogin`s usrpasswd = 123456
usrInfo`s classrome = 5
usrInfo`s studentID = 04161027
arr[0] = 0
arr[1] = 1
arr[2] = 2
arr[3] = 3
arr[4] = 4
arr[0] = 0
arr[1] = 1
arr[2] = 99
arr[3] = 3
arr[4] = 4
总结
对于我们定义的消息, 编译生成对应的 c++ 类 msg.pb.cc
其相应的常用 API 为:
SerializePartialToString()
序列化为字符串ParseFromString()
从字符串反序列化, 这两个也是我们可以在网络编程中收发数据时用到的- 每一个成员变量, 函数名直接为变量名时, 表示返回其
const reference
- 而
set_成员变量名()
表示赋值 mutable_成员变量名()
表示返回指向该成员的指针- 至于类似数组的
repeated
标识符字段如何操作代码中的注释有 - 对于每一个
message
还会生成一个CopyFrom()
函数可以进行复制操作, 但注意, 他可以接受任何类型的message
, 也就是有可能出现两个不同类型的消息之间进行复制, 所以注意它的使用, 或者使用赋值operator =
代替(赋值类型不同会报错)