Google Protocol Buffer 的常规用法需要使用 protoc.proto 编译成 .pb.h.pb.cc,这样做效率非常高,但是耦合性也很高。在某些追求通用性而不追求性能的场景下,需要使用 .proto 直接操作 protobuf 数据。

本例使用的 .proto 文件来自 https://developers.google.com/protocol-buffers/docs/cpptutorial ,但是把它拆成了两个 .proto 文件

// ./proto/person.proto
syntax = "proto2";
package tutorial; message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3; enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
} message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = HOME];
} repeated PhoneNumber phones = 4;
}
// ./proto/person.proto
syntax = "proto2";
package tutorial;
import "person.proto"; message AddressBook {
repeated Person people = 1;
}=

示例代码

#include <iostream>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/util/json_util.h> using namespace google::protobuf; /*
构造 Importer 必须指定 error_collector 用于处理错误信息
AddError 是纯虚函数,必须 override
*/
class MyMultiFileErrorCollector : public compiler::MultiFileErrorCollector
{
virtual void AddError(const std::string& filename, int line, int column, const std::string& message) override
{
std::cout << "file: " << filename << ", line: " << line << ", col: " << column<< ", message: " << message << std::endl;
}
}; int main()
{
/*
构造 DiskSourceTree,并添加虚拟路径。protobuf 使用 Importor 导入 .proto 文件时,会虚拟路径进行查找
在本例中,导入 addressbook.proto 时会使用 ./proto/addressbook.proto
*/
compiler::DiskSourceTree disk_source_tree;
disk_source_tree.MapPath("", "proto"); MyMultiFileErrorCollector error_collector; /*
导入 addressbook.proto 时,会自动导入所有依赖的 .proto 文件
在本例中,person.proto 也会被自动导入
*/
compiler::Importer importer(&disk_source_tree, &error_collector);
const FileDescriptor* file_descriptor = importer.Import("addressbook.proto");
if (!file_descriptor) {
exit(-1);
} // 把 addressbook.proto 和 person.proto 都打印出来
std::cout << "====== all .proto files ======" << std::endl;
std::cout << file_descriptor->DebugString() << std::endl;
for (int i = 0; i < file_descriptor->dependency_count(); ++i) {
std::cout << file_descriptor->dependency(i)->DebugString() << std::endl;
} // 构造 DynamicMessageFactory 用于动态构造 Message
DynamicMessageFactory message_factory(importer.pool()->generated_pool()); /*
查找 Person 的 Descriptor
不能使用 file_descriptor 查找,它只包含 addresssbook.proto ,只能找到 AddressBook,而 DescriptorPool 包含了所有数据
在使用 DescriptorPool 查找时需要使用全名,如:tutorial.Person
在使用 FileDescritor 查找需要使用顶级名字,如 AddressBook,而不是 tutorial.AddressBook
*/
const Descriptor* person_descriptor = importer.pool()->FindMessageTypeByName("tutorial.Person"); /*
使用工厂创建默认 Message,然后构造一个可以用来修改的 Message
这个 Message 的生命周期由 New 调用者管理
*/
const Message* default_person = message_factory.GetPrototype(person_descriptor);
Message* person = default_person->New(); // 使用 Reflection 修改 Message 的数据
const Reflection* reflection = person->GetReflection();
reflection->SetString(person, person_descriptor->FindFieldByName("name"), "abc");
reflection->SetInt32(person, person_descriptor->FindFieldByName("id"), 123456);
reflection->SetString(person, person_descriptor->FindFieldByName("email"), "abc@163.com"); // 把动态设置的 Message 的数据以 JSON 格式输出
util::JsonPrintOptions json_options;
json_options.add_whitespace = true;
json_options.preserve_proto_field_names = true;
string output;
util::MessageToJsonString(*person, &output, json_options);
std::cout << "====== Person data ======" << std::endl;
std::cout << output; // 析构 person
delete person;
}

输出

====== all .proto files ======
syntax = "proto2"; import "person.proto";
package tutorial; message AddressBook {
repeated .tutorial.Person people = 1;
} syntax = "proto2"; package tutorial; message Person {
message PhoneNumber {
optional string number = 1;
optional .tutorial.Person.PhoneType type = 2 [default = HOME];
}
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
repeated .tutorial.Person.PhoneNumber phones = 4;
} ====== Person data ======
{
"name": "abc",
"id": 123456,
"email": "abc@163.com"
}

https://developers.google.com/protocol-buffers/docs/reference/cpp

Protobuf 动态加载 .proto 文件并操作 Message的更多相关文章

  1. Protobuf 动态加载 .pb 文件并操作 Message

    之前写了<Protobuf 动态加载 .proto 文件并操作 Message>.除了直接读取 .proto 文件之外,还有一种类似的方法.先把 .proto 文件编译成 .pb 文件,再 ...

  2. Java_Java中动态加载jar文件和class文件

    转自:http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...

  3. 动态加载JS文件,并根据JS文件的加载状态来执行自己的回调函数

    动态加载JS文件,并根据JS文件的加载状态来执行自己的回调函数, 在很多场景下,我们需要在动态加载JS文件的时候,根据加载的状态来进行后续的操作,需要在JS加载成功后,执行另一方法,这个方法是依托在加 ...

  4. [转载] Java中动态加载jar文件和class文件

    转载自http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...

  5. javascript动态加载js文件主流浏览器兼容版

    一.代码示例: <html> <head> <meta http-equiv="Content-Type" content="text/ht ...

  6. Style样式的四种使用(包括用C#代码动态加载资源文件并设置样式)

    Posted on 2012-03-23 11:21 祥叔 阅读(2886) 评论(6) 编辑 收藏 在Web开发中,我们通过CSS来控制页面元素的样式,一般常用三种方式: 1.       内联样式 ...

  7. Android应用安全之外部动态加载DEX文件风险

    1. 外部动态加载DEX文件风险描述 Android 系统提供了一种类加载器DexClassLoader,其可以在运行时动态加载并解释执行包含在JAR或APK文件内的DEX文件.外部动态加载DEX文件 ...

  8. js动态加载css文件和js文件的方法

    今天研究了下js动态加载js文件和css文件的方法. 网上发现一个动态加载的方法.摘抄下来,方便自己以后使用 [code lang="html"] <html xmlns=& ...

  9. 两种动态加载JavaScript文件的方法

    两种动态加载JavaScript文件的方法 第一种便是利用ajax方式,第二种是,动静创建一个script标签,配置其src属性,经过把script标签拔出到页面head来加载js,感乐趣的网友可以看 ...

随机推荐

  1. mGWAS研究思路

    mGWAS(metabolome-based genome-wide association studies)的研究方法和GWAS类似,只是将代谢物含量作为表型性状来与基因位点进行关联.但同时它也有自 ...

  2. Genscan指南

    Genscan指南 GenScan是一个gene识别软件,主要是通过已知生物的基因结构特征来识别新的基因(parse).所利用的基因特征请参看readme文件. 特点: 只考虑编码蛋白的基因. 模型考 ...

  3. 【模板】单源最短路径(Dijkstra)/洛谷P4779

    题目链接 https://www.luogu.com.cn/problem/P4779 题目大意 给定一个 \(n\) 个点 \(m\) 条边有向图,每个点有一个非负权值,求从 \(s\) 点出发,到 ...

  4. C语言中的指针与整数相加的值计算

    以下分三种情况: 1. 指针 + 整数值 2. 整数 + 整数  3. 指针强制转换为另一个类型后(指针或者是整数)  +  整数 测试例子: 1 struct AAA{ int a; char b[ ...

  5. 一次线上GC故障解决过程记录

    排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http: ...

  6. 学习java的第十四天

    一.今日收获 1.完成了手册第二章没有验证完成的例题 2.预习了第三章的算法以及for语句与if语句的用法 二.今日难题 1.验证上出现问题,没有那么仔细. 2.第二章还有没有完全理解的问题 三.明日 ...

  7. Python计算期权隐含波动率

    更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. Black-Scholes 将期权价格描述为标的价格.行权价.无风险利率.到期时间和波动性的函数.  V ...

  8. 零基础学习java------36---------xml,MyBatis,入门程序,CURD练习(#{}和${}区别,模糊查询,添加本地约束文件) 全局配置文件中常用属性 动态Sql(掌握)

    一. xml  1. 文档的声明 2. 文档的约束,规定了当前文件中有的标签(属性),并且规定了标签层级关系 其叫html文档而言,语法要求更严格,标签成对出现(不是的话会报错) 3. 作用:数据格式 ...

  9. rem.js,移动多终端适配

    window.onload = function(){ /*720代表设计师给的设计稿的宽度,你的设计稿是多少,就写多少;100代表换算比例,这里写100是 为了以后好算,比如,你测量的一个宽度是10 ...

  10. 快速挂起VIM以及调出被挂起的VIM的方法

    vim中开了多窗口后有时需要临时切出去执行shell指令,查看结果,在vim中用%很不方便查看结果,要切出去又要逐个小窗口:q,非常麻烦. 上网一查竟然有挂起的方法: 挂起:ctrl-z 调出:fg ...