C++ protobuf示例

本文详细介绍了在Linux环境下安装Protobuf3的过程,包括下载、编译和安装步骤,并提供了简单的示例代码展示如何使用Protobuf进行数据序列化和反序列化。此外,还深入探讨了复杂数据结构的定义与操作,如repeated类型和map字段的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、linux上安装protobuf3:

1)下载:

https://download.youkuaiyun.com/download/liuxiao723846/11584862

2)编译安装:

function check_install_status() {
    if [ $? -ne 0 ]; then
        echo "--!!ERROR"
        exit 1
    fi
}

#install protobuf3
if [ ! -d protobuf-3.6.0 ]; then
    tar -zxf protobuf-3.6.0.tar.gz
    cd protobuf-3.6.0
    ./autogen.sh
    ./autogen.sh && ./configure
        check_install_status
    make -j8
        check_install_status
    make install
        check_install_status
    cd python
    python setup.py install
        check_install_status
    ldconfig
    cd ../../
fi

2、简单示例:

1)person.proto:

package test;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
}

编译,生成C++代码:

$ protoc ./person.proto --cpp_out=./
$ ll
-rw-rw-r--. 1 roo roo  17237 Aug 20 21:19 person.pb.cc
-rw-rw-r--. 1 roo roo  12965 Aug 20 21:19 person.pb.h

2)编写C++业务代码:

#include <iostream>
#include <fstream>
#include "person.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"


using namespace test;
int main(){
  Person p;
  p.set_name("test");
  p.set_id(1);
  p.set_email("a.iabc.com");

  //------------------将pb二进制信息保存到字符串
  std::string str;
  p.SerializeToString(&str);
  std::cout<<str<<std::endl;  
 
  //------------------将pb文本信息写入文件
  std::ofstream fw; 
  fw.open("./Person.txt", std::ios::out | std::ios::binary);
  google::protobuf::io::OstreamOutputStream *output = new google::protobuf::io::OstreamOutputStream(&fw);
  google::protobuf::TextFormat::Print(p, output);

  delete output;
  fw.close();

  //---------------------将pb文本信息保存到字符串
  std::string str1;
  google::protobuf::TextFormat::PrintToString(p, &str1);
  std::cout<<str1<<std::endl;

  //---------------------反序列化
  Person p1;
  p1.ParseFromString(str);
  std::cout<<"name:"<<p1.name()<<",email:"<<p1.email()<<std::endl;

  return 0;
}

3)编译、运行:

g++ -g -o run person_test.cpp ./person.pb.cc -I. -lprotobuf -pthread -std=c++11
或者
g++ -g -o run person_test.cpp ./person.pb.cc -I. -I/usr/local/protobuf/include -L/usr/local/protobuf/lib -lprotobuf -pthread -std=c++11

4)api说明:

  • p.SerializeToString(&str):将对象p序列化成二进制数据,保存到字符串中;
  • 二进制虽然小,但是可读性差,所以通过google::protobuf::TextFormat::print()方法生成文本数据写入文件;为此需要引入google/protobuf/text_format.h(否则会报‘google::protobuf::TextFormat’ has not been declared错误)、zero_copy_stream_impl.h(否则会报OstreamOutputStream找不到)
  • 除了将pb信息序列化成文本写入文件外,还可以调用google::protobuf::TextFormat::PrintToString(mrlist, &str1); 方法将pb文本信息保存到字符串中;(别忘了加text_format.h头文件)
  • p1.ParseFromString(str);:将字符串中的二进制信息反序列成Person对象;
  • C++版pb的api一般为:pb对象.set_消息属性(value);设置属性值;pb对象.消息属性();获取属性值

3、复杂示例:

1)pb消息结构test.proto:

syntax = "proto2";
package video_stream;

message Item{
	optional string id = 1;
    optional float score = 2;
}
message ModelItems{
    optional string modelName = 1;
    repeated Item itemList = 2;
}
message ModelRecallInfoList{
    repeated ModelItems modelItemsList = 1;
}

message InnerUserPrefResponse {
	optional map<string, ModelItems> user_feed_map = 1;
}

编译:

$ protoc ./test.proto --cpp_out=./
$ ll
-rw-rw-r--. 1 roo roo  55676 Aug 20 23:28 test.pb.cc
-rw-rw-r--. 1 roo roo  32236 Aug 20 23:28 test.pb.h

2)c++业务代码:

#include <iostream>
#include <fstream>
#include "test.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"

using namespace video_stream;

int main(){

  ModelRecallInfoList mrlist;
  ModelItems* items;
  Item* item;
  
  //model_item1
  items = mrlist.add_modelitemslist();
  items->set_modelname("model_items1");
  //item1
  item = items->add_itemlist(); 
  item->set_id("item1");
  item->set_score(0.9);
  //item2
  item = items->add_itemlist();
  item->set_id("item2");
  item->set_score(1.2);
  
  //model_items2
  items = mrlist.add_modelitemslist();
  items->set_modelname("model_items2");
  //item1
  item = items->add_itemlist();
  item->set_id("model_item2_item1");
  item->set_score(99.9);
  
  std::string str;
  mrlist.SerializeToString(&str);
  //std::cout<<str<<std::endl;

  std::string str1;
  google::protobuf::TextFormat::PrintToString(mrlist, &str1);
  std::cout<<str1<<std::endl;

  //---------------------------------------反序列化
  ModelRecallInfoList m;
  m.ParseFromString(str);
  int size = m.modelitemslist_size();
  for(int i=0;i<size;i++){
    ModelItems model_item  = m.modelitemslist(i);
    std::string model_name = model_item.modelname();
    int s1 = model_item.itemlist_size();
    std::string s;

    for(int j = 0;j<s1;j++){
      s+=model_item.itemlist(j).id()+",";
    }
    std::cout<<"modelName:"<<model_name<<",itemId:"<<s<<std::endl;
    
    //std::cout<<"modelName:"<<m.modelitemslist(i).modelname()<<std::endl;
  }
  
  //---------------------------------------放到map里
  InnerUserPrefResponse resp;
  auto* map = resp.mutable_user_feed_map(); 
  for (int i=0;i<size;i++){
    ModelItems model_item  = m.modelitemslist(i);
    std::string mn = model_item.modelname();
    (*map)[mn] = model_item; 
  }

  std::string str2;
  google::protobuf::TextFormat::PrintToString(resp, &str2);
  std::cout<<"=========================="<<std::endl;
  std::cout<<str2<<std::endl;
  return 0;
}

编译、运行:

g++ -g -o run test_main.cpp ./test.pb.cc -I. -lprotobuf -pthread -std=c++11

3)api说明:

pb对象中,子对象需要使用指针;

对于repeated类型:

pb对象.add_属性名() 添加一个子对象的指针

pb对象.属性名_size()返回repeated大小;

pb对象.属性名(i)遍历其中一个子对象;

4、复杂消息示例2:

1)student.proto

package test;

message Address {
  optional string name = 1;
  optional string code = 2;
}
message Student {
  required string name = 1;
  required int32 id = 2;
  repeated string email = 3;
  optional Address ads = 4;
  map<string,string> other = 5;
}

编译

$ protoc ./student.proto --cpp_out=./
$ ll
-rw-rw-r--. 1 roo roo  55676 Aug 20 23:28 student.pb.cc
-rw-rw-r--. 1 roo roo  32236 Aug 20 23:28 student.pb.h

2)C++业务代码student_test.cpp:

#include <iostream>
#include "student.pb.h"
#include "google/protobuf/text_format.h"

using namespace test;

int main(){
  Student p;
  p.set_name("test");
  p.set_id(1);
  p.add_email("a.yxz.com");
  p.add_email("b.abc.com");
  std::string* e = p.add_email();
  *e = "c.iop.com";
  
  Address* ads = p.mutable_ads();
  ads->set_name("taiyuan");
  ads->set_code("0351");

  auto* map = p.mutable_other();  
  (*map)["biye"] = "nuc";
  (*map)["job"] = "abc";

  //============序列化成二进制
  std::string str;
  p.SerializeToString(&str);
  //std::cout<<str<<std::endl;  
 
  //============序列化成文本,打印
  std::string str1;
  google::protobuf::TextFormat::PrintToString(p, &str1);
  std::cout<<str1<<std::endl;
  
  //===========反序列化
  Student p1;
  p1.ParseFromString(str);
  int es = p1.email_size();
  std::string emails;
  for (int i=0;i<es;i++){
    emails += p1.email(i)+",";
  }

  Address adss= p1.ads();

  google::protobuf::Map<std::string,std::string> o = p1.other();
  google::protobuf::Map<std::string,std::string>::iterator ite;
  
  std::string others;
  for(ite=o.begin();ite!=o.end();ite++){
    others+=ite->first+":"+ite->second;
  }
  std::cout<<"name:"<<p1.name()<<";id:"<<p1.id()<<";emails:"<<emails<<";addreds:"<<adss.name()<<";other:"<<others<<std::endl;
  return 0;
}

3)说明:

  • 可以看出来,对于子对象、map:设置值得时候一般需要用mutable_属性();获取值一般用属性()
  • 对于pb提供的api,可以在生成的.h文件中查看相应的方法;

补充:对于map的操作:

::google::protobuf::Map< ::std::string, ::std::string >* map = test.mutable_values();
cout << "size: " << f.values_size() << endl;   // size = 0
// add to map
(*map)["a"] = "a1";
(*map)["c"] = "a1";
(*map)["a"] = "a1";
cout << "size: " << f.values_size() << endl;   // size = 3

也可以使用下面:
auto& map = *test.mutable_values();
map["a"] = "a1";

参考:

https://developers.google.com/protocol-buffers/docs/reference/cpp-generated#map-fields

https://www.cnblogs.com/shine-lee/p/10701810.html

http://xcd.blog.techweb.com.cn/archives/173.html

https://www.cnblogs.com/tangxin-blog/p/8314563.html

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值