经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
Protobuf 动态加载 .proto 文件并操作 Message
来源:cnblogs  作者:mkckr0  时间:2021/12/31 9:04:18  对本文有异议

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

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

  1. // ./proto/person.proto
  2. syntax = "proto2";
  3. package tutorial;
  4. message Person {
  5. optional string name = 1;
  6. optional int32 id = 2;
  7. optional string email = 3;
  8. enum PhoneType {
  9. MOBILE = 0;
  10. HOME = 1;
  11. WORK = 2;
  12. }
  13. message PhoneNumber {
  14. optional string number = 1;
  15. optional PhoneType type = 2 [default = HOME];
  16. }
  17. repeated PhoneNumber phones = 4;
  18. }
  1. // ./proto/person.proto
  2. syntax = "proto2";
  3. package tutorial;
  4. import "person.proto";
  5. message AddressBook {
  6. repeated Person people = 1;
  7. }

示例代码

  1. #include <iostream>
  2. #include <google/protobuf/compiler/importer.h>
  3. #include <google/protobuf/dynamic_message.h>
  4. #include <google/protobuf/util/json_util.h>
  5. using namespace google::protobuf;
  6. /*
  7. 构造 Importer 必须指定 error_collector 用于处理错误信息
  8. AddError 是纯虚函数,必须 override
  9. */
  10. class MyMultiFileErrorCollector : public compiler::MultiFileErrorCollector
  11. {
  12. virtual void AddError(const std::string& filename, int line, int column, const std::string& message) override
  13. {
  14. std::cout << "file: " << filename << ", line: " << line << ", col: " << column<< ", message: " << message << std::endl;
  15. }
  16. };
  17. int main()
  18. {
  19. /*
  20. 构造 DiskSourceTree,并添加虚拟路径。protobuf 使用 Importor 导入 .proto 文件时,会使用虚拟路径进行查找
  21. 在本例中,导入 addressbook.proto 时会使用 ./proto/addressbook.proto
  22. */
  23. compiler::DiskSourceTree disk_source_tree;
  24. disk_source_tree.MapPath("", "proto");
  25. MyMultiFileErrorCollector error_collector;
  26. /*
  27. 导入 addressbook.proto 时,会自动导入所有依赖的 .proto 文件
  28. 在本例中,person.proto 也会被自动导入
  29. */
  30. compiler::Importer importer(&disk_source_tree, &error_collector);
  31. const FileDescriptor* file_descriptor = importer.Import("addressbook.proto");
  32. if (!file_descriptor) {
  33. exit(-1);
  34. }
  35. // 把 addressbook.proto 和 person.proto 都打印出来
  36. std::cout << "====== all .proto files ======" << std::endl;
  37. std::cout << file_descriptor->DebugString() << std::endl;
  38. for (int i = 0; i < file_descriptor->dependency_count(); ++i) {
  39. std::cout << file_descriptor->dependency(i)->DebugString() << std::endl;
  40. }
  41. /*
  42. 查找 Person 的 Descriptor
  43. 不能使用 file_descriptor 查找,它只包含 addresssbook.proto ,只能找到 AddressBook,而 DescriptorPool 包含了所有数据
  44. 在使用 DescriptorPool 查找时需要使用全名,如:tutorial.Person
  45. 在使用 FileDescritor 查找时需要使用顶级名字,如:AddressBook,而不是 tutorial.AddressBook
  46. */
  47. const Descriptor* person_descriptor = importer.pool()->FindMessageTypeByName("tutorial.Person");
  48. /*
  49. 使用工厂创建默认 Message,然后构造一个可以用来修改的 Message
  50. 这个 Message 的生命周期由 New 调用者管理
  51. */
  52. DynamicMessageFactory message_factory;
  53. const Message* default_person = message_factory.GetPrototype(person_descriptor);
  54. Message* person = default_person->New();
  55. // 使用 Reflection 修改 Message 的数据
  56. const Reflection* reflection = person->GetReflection();
  57. reflection->SetString(person, person_descriptor->FindFieldByName("name"), "abc");
  58. reflection->SetInt32(person, person_descriptor->FindFieldByName("id"), 123456);
  59. reflection->SetString(person, person_descriptor->FindFieldByName("email"), "abc@163.com");
  60. // 把动态设置的 Message 的数据以 JSON 格式输出
  61. util::JsonPrintOptions json_options;
  62. json_options.add_whitespace = true;
  63. json_options.always_print_primitive_fields = true;
  64. json_options.preserve_proto_field_names = true;
  65. std::string output;
  66. util::MessageToJsonString(*person, &output, json_options);
  67. std::cout << "====== Person data ======" << std::endl;
  68. std::cout << output;
  69. // 析构 person
  70. delete person;
  71. }

输出

  1. ====== all .proto files ======
  2. syntax = "proto2";
  3. import "person.proto";
  4. package tutorial;
  5. message AddressBook {
  6. repeated .tutorial.Person people = 1;
  7. }
  8. syntax = "proto2";
  9. package tutorial;
  10. message Person {
  11. message PhoneNumber {
  12. optional string number = 1;
  13. optional .tutorial.Person.PhoneType type = 2 [default = HOME];
  14. }
  15. enum PhoneType {
  16. MOBILE = 0;
  17. HOME = 1;
  18. WORK = 2;
  19. }
  20. optional string name = 1;
  21. optional int32 id = 2;
  22. optional string email = 3;
  23. repeated .tutorial.Person.PhoneNumber phones = 4;
  24. }
  25. ====== Person data ======
  26. {
  27. "name": "abc",
  28. "id": 123456,
  29. "email": "abc@163.com",
  30. "phones": []
  31. }

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

本文来自博客园,作者:mkckr0,转载请注明原文链接:https://www.cnblogs.com/mkckr0/p/15731925.html

原文链接:http://www.cnblogs.com/mkckr0/p/15731925.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号