【c++】14.编译proto和proto相关用法

2024-06-16 21:38
文章标签 c++ 编译 proto 用法 相关 14

本文主要是介绍【c++】14.编译proto和proto相关用法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

编译proto和proto相关用法

关于proto相关的知识可以参考系列博客 https://blog.csdn.net/daaikuaichuan/category_9869251.html

【xx.proto文件中如果要注释的话,注释符号也是双斜杠"//"】

1.编译proto的问题

重装protoc可参考 https://blog.csdn.net/u013498583/article/details/74231058

查看当前protoc版本: protoc --version

查看protoc安装位置:which protoc

查找protoc相关文件:sudo find / -name protoc

编译proto文件
protoc caffe.proto --cpp_out=./ 生成caffe.pb.h、caffe.pb.cc文件
protoc caffe.proto --python_out=./ 生成caffe_pb2.py文件
假如环境中装了一个以上的protoc版本,可使用protoc双击tab键,会弹出所有版本(protoc protoc_2.6.1,其中protoc是默认版本,protoc --version是查看当前默认protoc版本)。
如果我们要使用非默认版本protoc编译proto,可以protoc_2.6.1 caffe.proto --cpp_out=./这种方式编译。
在编译MobileNet-YOLO工程时,使用protoc protoc_2.6.1编译后的 caffe.pb.hcaffe.pb.cc 文件粘贴到 caffe\include\caffe\proto即可。

----------------------------------------------------------------------------------------

2.proto相关函数用法

如果有一个proto_name结构的 proto消息:

2.1 set_xx(value)、 add_xx()、 mutable_xxx()

proto_name.set_xx(value) 该函数给对应的变量赋值

proto_name.add_xx() 该函数给对应的repeated变量增加一个xx类型的元素,返回值为指针类型。一般用法为:在for循环中调用一次,proto消息就添加一个该类型数据。

proto_name.mutable_xxx() 该函数返回指向该字段的一个指针,同时将该字段置为被设置状态。若该对象存在,则直接返回该对象,若不存在则新new 一个。

----------------------------------------------------------------------------------------
2.2 SerializeToString、ParseFromString、SerializeToArray、ParseFromArray、CopyFrom、IsInitialized、

例如,有 proto_name.proto文件,把这个 proto_name.proto文件编译后,会产生 proto_name.pb.hproto_name.pb.cc, 生成的.h文件中的class都继承自::google::protobuf::Message类,Message类提供了一些方法可以检查或者操作整个message,包括:

bool IsInitialized() const; 检查是否所有required变量都已经初始化;

string DebugString() const; 返回message的可阅读的表示,主要用于调试程序;

void CopyFrom(const Person& from); 使用一个message的值覆盖本message;

void Clear(); 清空message的所有成员变量值。

----------------------------------------------------------------------------------------

每个message类都提供了写入和读取message数据的方法,包括

bool SerializeToString(string* output) const; // 把message编码进output。

bool ParseFromString(const string& data); // 从string解码到message

bool SerializeToArray(char* buf,int size) const; // 把message编码进数组buf.

bool ParseFromArray(const char* buf,int size); // 把 buf解码到message。此解码方法效率较ParseFromString高很多,所以一般用这种方法解码。

bool SerializeToOstream(ostream* output) const; // 把message编码进ostream

bool ParseFromIstream(istream* input); // 从istream解码到message

备注:发送接收端所使用的加码解码方法不一定非得配对,即发送端用SerializeToString 接收端不一定非得用ParseFromString ,可以使用其他解码方法。

----------------------------------------------------------------------------------------
一般使用 bool SerializeToString(string* output) const;bool SerializeToArray(char* buf,int size) const;

例如当需要使用tcp/udp发送包含数组的时候,建议使用 bool SerializeToArray(char* buf,int size) const;,因为涉及到 数据包头、校验位之类的需要使用数组索引的方式,这样最好使用SerializeToArray

#define _fill_pack(name)                                                    \{                                                                         \char tbuf[256 * 1024];                                                  \uint32_t len = name.ByteSize();                                         \auto header = name.header();                                            \_fill_pack_head(static_cast<uint32_t>(netproxy_##name##_pack), len,     \reinterpret_cast<netproxy_pack_t *>(tbuf));             \if (!name.SerializeToArray(&tbuf[sizeof(netproxy_pack_t)], len)) {      \SERROR << "serialize error, name:" << #name << " len:" << len;        \len = -sizeof(netproxy_pack_t);                                       \}                                                                       \Send_data(tbuf, len + sizeof(netproxy_pack_t), netproxy_##name##_pack); \}

其他时候可以直接使用SerializeToString

----------------------------------------------------------------------------------------
用法1:对外接口的消息结构的兼容性

当某个对外接口的消息结构 msgA 中的某个变量的结构总是要根据需求而频繁增删修改的时候,一般会在这个消息结构 msgA 中定义一个std::string proto_data的变量,
这样只需要修改proto_name.proto文件中的内容,而不需要总是修改 msgA(尤其是当修改msgA可能需要修改框架协议的时候,总之就是代价比较大),使用proto方法来解决这个问题就特别方便。

原始结构:

struct msgA{MsgHeader header;bool is_ok;structBBB msgbbb;  //当这个结构体 structBBB 增删修改结构比较麻烦时,会造成频繁修改对外接口 msgA 的麻烦。
}
方法:

先定义一个 proto_name.proto文件,把这个 proto_name.proto文件编译后,会产生 proto_name.pb.hproto_name.pb.cc,
对其中定义的变量赋值是使用 set_xxx(111) 函数进行赋值,赋值完成后再序列化之后进行打包发送;
发送之前使用 proto_name.SerializeToString(&data) 把该 结构序列化为string类型,然后再发送。
原始结构 修改为:

struct msgA{MsgHeader header;bool is_ok;//定义一个string类型的结构来代替 structBBB msgbbb; 然后把proto定义的数据结构 proto_name.proto赋值后,//调用 proto_name.SerializeToString(proto_data) 进行序列化转换为string赋值给proto_data变量,这就是使用proto方式来解决。std::string proto_data;   
}

下面的函数,我们封装了proto自带的SerializeToString和ParseFromString方法

// msg转换成string
template <class T>
void MsgToString(const T msg, std::string &data) {msg.SerializeToString(&data);
}
// 将string转换成msg
template <class T>
void StringToMsg(const std::string data, T &msg) {msg.ParseFromString(data);
}
----------------------------------------------------------------------------------------
用法2:

当我们需要使用配置文件来给许多变量进行赋值的时候,就可以把这些变量全都定义在 config.proto中,然后建立一个config.pb.txt文件,在config.pb.txt文件中对config.proto中的变量进行设置。
后续进行一些读取config.pb.txt文件中的内容,然后需要的地方获取这些内容。
代码涉及到文件读取解析proto等等的内容,详解见下:

/**下面这函数是读取配置文件,然后将值赋给message,然后就可以对message以message.mutable_xxx()的形式进行获取数据了**/
/**env_fusion_data_是proto消息,继承于 google::protobuf::Message类**/
pinecone::common::GetProtoFromFile(data_file_name, &message); /**定义见下**/
bool GetProtoFromFile(const std::string &file_name,google::protobuf::Message *message) {// Try the binary parser first if it's much likely a binary proto.static const std::string kBinExt = ".bin";if (std::equal(kBinExt.rbegin(), kBinExt.rend(), file_name.rbegin())) {return GetProtoFromBinaryFile(file_name, message) ||GetProtoFromASCIIFile(file_name, message);}return GetProtoFromASCIIFile(file_name, message) ||GetProtoFromBinaryFile(file_name, message);
}
上面这个函数调用下面这两个函数:
函数1: 读取二进制文件,调用了proto定义的内置函数 bool ParseFromIstream(istream* input)
bool GetProtoFromBinaryFile(const std::string &file_name,google::protobuf::Message *message) {std::fstream input(file_name, std::ios::in | std::ios::binary);if (!input.good()) {SERROR << "Failed to open file " << file_name << " in binary mode.";return false;}if (!message->ParseFromIstream(&input)) {SERROR << "Failed to parse file " << file_name << " as binary proto.";return false;}return true;
}
函数2: 读取ASCII文件,调用了open(),close()和proto内置函数static bool Parse(io::ZeroCopyInputStream* input, Message* output);
bool GetProtoFromASCIIFile(const std::string &file_name,google::protobuf::Message *message) {using google::protobuf::TextFormat;using google::protobuf::io::FileInputStream;using google::protobuf::io::ZeroCopyInputStream;int file_descriptor = open(file_name.c_str(), O_RDONLY);if (file_descriptor < 0) {SERROR << "Failed to open file " << file_name << " in text mode.";// Failed to open;return false;}ZeroCopyInputStream *input = new FileInputStream(file_descriptor);bool success = TextFormat::Parse(input, message);if (!success) {SERROR << "Failed to parse file " << file_name << " as text proto.";}delete input;close(file_descriptor);return success;
}

这篇关于【c++】14.编译proto和proto相关用法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1067621

相关文章

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Redis的Hash类型及相关命令小结

《Redis的Hash类型及相关命令小结》edisHash是一种数据结构,用于存储字段和值的映射关系,本文就来介绍一下Redis的Hash类型及相关命令小结,具有一定的参考价值,感兴趣的可以了解一下... 目录HSETHGETHEXISTSHDELHKEYSHVALSHGETALLHMGETHLENHSET

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

Springboot中Jackson用法详解

《Springboot中Jackson用法详解》Springboot自带默认json解析Jackson,可以在不引入其他json解析包情况下,解析json字段,下面我们就来聊聊Springboot中J... 目录前言Jackson用法将对象解析为json字符串将json解析为对象将json文件转换为json

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�