JamesB 发表于 2022-9-6 13:54

Protobuf C++的简单使用-带例程

Protobuf C++的简单使用-带例程


Protobuf简介

以C++而言,简单的说就是可以将多种类型的数据如vector、map序列化至协议缓存区中,协议缓冲区的数据可以用来进程之间通信,也可以以二进制形式直接写到硬盘中,实现不占用内存的目的。Protobuf读写速度快,一般都在ms级别,是目前最高效的序列化工具。 protobuf由一些基础的消息类型组成更高级的数据类型,这里以一个包含Eigen的结构体在硬盘中的读写为例。
syntax = "proto3";

package proto;

message Vector3d {
double x = 1;
double y = 2;
double z = 3;
}

message RangeData {
repeated int32 point_x = 1;
repeated int32 point_y = 2;
Vector3d local_pose = 3;
int64 time_stamp = 4;
}其中repeated声明了一个类似于std::vector的数据,可连续存储,以上消息在C++对应了一个结构体:
struct RangeData {
RangeData();
explicit RangeData(const proto::RangeData& range_data_proto);
std::vector<double> points_x;
std::vector<double> points_y;
Eigen::Vector3d local_pose;
int64_t time_stamp;
};
其中explicit RangeData(const proto::RangeData& range_data)输入参数为proto::RangeData,即是前面所定义的消息类型message RangeData,对于此结构体,我们需要写一个以proto作为输入的构造函数,和将当前数据输出为Proto的输出函数ToProto,两个函数实现如下:
// 基于proto的构造函数
Eigen::Vector3d ToEigen(const proto::Vector3d& proto) {
return Eigen::Vector3d(proto.x(), proto.y(), proto.z());
}

RangeData::RangeData(const proto::RangeData& range_data_proto) {
this->time_stamp = range_data_proto.time_stamp();
this->local_pose = ToEigen(range_data_proto.local_pose());
for(const auto& point_x : range_data_proto.points_x()) {
    this->points_x.emplace_back(point_x);
}
for(const auto& point_y : range_data_proto.points_y()) {
    this->points_y.emplace_back(point_y);
}
}

// 将range_data输出为proto
proto::Vector3d ToProto(const Eigen::Vector3d& vector) {
proto::Vector3d proto;
proto.set_x(vector.x());
proto.set_y(vector.y());
proto.set_z(vector.z());
return proto;
}

proto::RangeData ToProto(const RangeData& range_data) {
proto::RangeData proto;
*proto.mutable_points_x() = {range_data.points_x.begin(), range_data.points_x.end()};
*proto.mutable_points_y() = {range_data.points_y.begin(), range_data.points_y.end()};
*proto.mutable_local_pose() = ToProto(range_data.local_pose);
proto.set_time_stamp(range_data.time_stamp);
return proto;
}
以上就实现了基于proto的读写,得到了协议缓冲区数据proto::RangeData,进一步可以将此函数以二进制形式写到硬盘中,这部分是参考cartograpth代码,在本文例程的proto_io.h proto_io.cc中实现。
本文例程

protobuf需要安装google的protobuf库,安装参考 https://www.lmlphp.com/user/56/article/item/6067/
同时可以将protobuf用cmake生成动态.so库,需要的时候直接链接就好,本例程的Proto cmake:
# 查找 protobuf
find_package(Protobuf REQUIRED)

# 编译 proto 为 .cpp 和 .h
FILE(GLOB PROTO_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
message(STATUS ${PROTO_FILES})

PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${PROTO_FILES})
message("PROTO_SRCS = ${PROTO_SRCS}")
message("PROTO_HDRS = ${PROTO_HDRS}")

add_library(proto STATIC ${PROTO_SRCS} ${PROTO_HDRS})

target_link_libraries(proto ${Protobuf_LIBRARIES})

target_include_directories(proto
      PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
      PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
      PUBLIC ${PROTOBUF_INCLUDE_DIRS})在主程序中将其作为子模块,主程序的cmake:
cmake_minimum_required(VERSION 3.2)

project(proto_demo)

find_package(Eigen3 REQUIRED)
set(BOOST_COMPONENTS iostreams)
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})

include_directories(include
                  build # 为了更方便的使用proto的代码提示
                  ${EIGEN3_INCLUDE_DIR}
                  ${Boost_INCLUDE_DIRS})

add_subdirectory(proto)
add_library(${PROJECT_NAME}
                        src/grid_2d.cc
            src/submap_2d.cc
                        src/proto_io.cc
                        )
target_link_libraries(${PROJECT_NAME} proto ${Boost_LIBRARIES})
add_executable(demo app/demo.cc)
target_link_libraries(demo ${PROJECT_NAME})具体例程如下:
git clone https://github.com/hipforth/proto_demo.git
cd proto_demo && mkdir build && cd build
cmake .. && make
./demo demo.bindemo.bin即为写入的protobuf二进制格式数据

rustum 发表于 2022-9-6 13:59

您好,可以要背景猫的照片吗,谢谢
页: [1]
查看完整版本: Protobuf C++的简单使用-带例程