protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,可用于通信协议、数据存储等。
使用流程:
不同版本的protobuf无法互相兼容,ROS安装时会一并安装,路径为
/usr/bin/protoc
,若需要依赖其他版本可安装在不同路径下
安装依赖 sudo apt-get install autoconf automake libtool curl make g++
生成Makefile ./autogen.sh && ./configure
如需多版本并存或修改安装路径,可设置目录/configure --prefix=/usr/protobuf
检查编译结果 make check
安装到系统路径下 sudo make install
设置系统环境
单版本安装
Ubuntu默认安装在/usr/local/lib目录下
sudo gedit /etc/ld.so.conf.d/libprotobuf.conf
/usr/local/lib
(若修改了路径,使用修改过的)sudo ldconfig
多版本安装
sudo ln -s /usr/protobuf/bin/protoc /usr/local/bin/protoc3.6
sudo ldconfig
测试 protoc --version
定义结构体
message <name> {}
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
定义枚举
enum <name> {}
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
字段规则
注意:proto3已舍弃required字段,optional字段也无法显示使用(因为缺省默认就设置为optional),同时弃用默认值设置,默认值大多为0、空、false。
[packed = true]
选项可提高编码效率字段类型
protobuf属性 | C++属性 | 备注 | |
---|---|---|---|
bool | bool | ||
string | string | 一个字符串必须是utf-8编码或者7-bit的ascii编码的文本,长度不能超过 2^32 | |
bytes | string | 可能包含任意顺序的字节数据,但长度不能超过 2^32 | |
double | double | 固定8个字节 | |
float | float | 固定4个字节 | |
int32 | int32 | 使用变长编码,对于负数编码效率较低,如果经常使用负数,建议使用sint32 | |
int64 | int64 | 使用变长编码,对于负数编码效率较低,如果经常使用负数,建议使用sint64 | |
uint32 | uint32 | 使用变长编码 | |
uint64 | uint64 | 使用变长编码 | |
sint32 | int32 | 使用变长编码,符号整型。负值的编码效率高于常规的 int32 类型 | |
sint64 | int64 | 使用变长编码,符号整型。负值的编码效率高于常规的 int64 类型 | |
fixed32 | uint32 | 定长4字节,如果数据>2^28,编码效率高于unit32 | |
fixed64 | uint64 | 定长8字节,如果数据>2^56,编码效率高于unit64 | |
sfixed32 | int32 | 总是4字节 | |
sfixed64 | int64 | 总是8字节 |
字段编号
protoc -I=<SRC_DIR> --cpp_out=<DST_DIR> <SRC_DIR>/xxx.proto
_pb2.py
文件# 寻找 Protobuf 库
find_package(Protobuf REQUIRED)
# 生成protobuf的lib
file(GLOB PROTO_LIST "proto/*.cc")
add_library(proto
STATIC
${PROTO_LIST})
target_include_directories(proto PUBLIC ${PROTOBUF_INCLUDE_DIRS})
target_link_libraries(proto PUBLIC ${PROTOBUF_LIBRARIES})
# 链接执行文件
add_executable(protobuf_test
src/protobuf_test.cpp)
target_include_directories(protobuf_test PUBLIC ${PROJECT_SOURCE_DIR}/proto)
target_link_libraries(protobuf_test proto)
自动生成对应的C++头文件和源文件
protobuf_generate_cpp (<SRCS> <HDRS>
[DESCRIPTORS <DESC>] [EXPORT_MACRO <MACRO>] [<ARGN>...])
__declspec(dllexport)
or __declspec(dllimport)
depending on what is being compiled..proto
filesprotobuf消息编译库proto
,后续调用直接使用该库即可。
project(proto)
# 寻找 Protobuf 库
find_package(Protobuf REQUIRED)
# 检测是否找到 Protobuf
if(PROTOBUF_FOUND)
message("** protobuf found")
else()
message(FATAL_ERROR "Cannot find Protobuf")
endif()
# 寻找所有的proto文件
file(GLOB PROTO_FILES ${PROJECT_SOURCE_DIR}/*.proto)
message("** PROTO_FILES = ${PROTO_FILES}")
# Generate the .h and .cxx files 必须配合 add_executable() 或者 add_library() 才能正常使用
protobuf_generate_cpp(PROTOBUF_SRCS PROTOBUF_HDRS ${PROTO_FILES})
# 增加 proto 描述文件的静态库
add_library(${PROJECT_NAME} STATIC ${PROTOBUF_SRCS} ${PROTOBUF_HDRS})
target_include_directories(${PROJECT_NAME} PUBLIC ${PROTOBUF_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PUBLIC ${PROTOBUF_LIBRARIES})
在生成的C++接口内,会根据设置数据类型生成对应的函数接口,singular(单组数据)和repeated(多组数据)定义的类型生成的api存在差异。
double/int
// double x = 1;
void clear_x();
static const int kXFieldNumber = 1;
double x() const;
void set_x(double value);
// int32 id = 2;
void clear_id();
static const int kIdFieldNumber = 2;
::google::protobuf::int32 id() const;
void set_id(::google::protobuf::int32 value);
bytes/string
在C++中bytes和string的实现都是std::string类型,区别在于string类型序列化调用了VerifyUTF8StringNamedField
函数检验string中是否有非法的UTF-8字符,而bytes类型没有检验。
string类型不能直接通过set赋值,需设置string变量后,将string变量通过set进行赋值。
// bytes data = 1;
void clear_data();
static const int kDataFieldNumber = 1;
const ::std::string& data() const;
void set_data(const ::std::string& value);
#if LANG_CXX11
void set_data(::std::string&& value);
#endif
void set_data(const char* value);
void set_data(const void* value, size_t size);
::std::string* mutable_data();
::std::string* release_data();
void set_allocated_data(::std::string* data);
嵌套message
// .test.test_2_msg c = 3;
bool has_c() const;
void clear_c();
static const int kCFieldNumber = 3;
const ::test::test_2_msg& c() const;
::test::test_2_msg* release_c();
::test::test_2_msg* mutable_c();
void set_allocated_c(::test::test_2_msg* c);
通过set_allocated_
和mutable_
传入已定义值,在关闭程序时会引起的double free core dump,参考c++ Protobuf中set_allocated引起的double free core dump
正确定义:
int good_case1(){
a* aa = new a();
b bb;
aa->set_aa(1);
bb.set_allocated_aaa(aa);
return 0;
}
void good_case2(){
a aa;
b bb;
aa.set_aa(1);
bb.mutable_aaa()->MergeFrom(aa);
}
descriptor
#include <google/protobuf/message.h>
namespace google::protobuf
const Descriptor* descriptor = msg->GetDescriptor();
double/int
// repeated int32 e = 5;
int e_size() const;
void clear_e();
static const int kEFieldNumber = 5;
::google::protobuf::int32 e(int index) const;
void set_e(int index, ::google::protobuf::int32 value);
void add_e(::google::protobuf::int32 value);
const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
e() const;
::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
mutable_e();
嵌套message
// repeated .test.test_2_msg d = 4;
int d_size() const;
void clear_d();
static const int kDFieldNumber = 4;
::test::test_2_msg* mutable_d(int index);
::google::protobuf::RepeatedPtrField< ::test::test_2_msg >*
mutable_d();
const ::test::test_2_msg& d(int index) const;
::test::test_2_msg* add_d();
const ::google::protobuf::RepeatedPtrField< ::test::test_2_msg >&
d() const;
调用接口实现序列化、反序列化以及读写
序列化
bool SerializeToString(string* output) const
序列化消息并将字节存储在给定的字符串中。请注意,字节是二进制的,而不是文本,只是使用 string
类作为容器。
文件中显示为乱码,通过gedit修改会损坏数据,可以通过vim修改。
bool SerializeToOstream(ostream* output) const
将message写入给定的C++的ostream
反序列化
bool ParseFromString(const string& data)
解析给定字符串到message
bool ParseFromIstream(istream* input)
解析给定C++ istream到message