java protobuf extend_如何使用protobuf extend和编写protobuf插件

前言

google开源的Protocol Buffers(后面简称protobuf)作为一种结构化数据序列化和反序列化的工具,在很多场景中都有应用,而extend的拓展机制又增加了很多可能性。下面通过一个示例来简单说明一下如何使用protobuf拓展和编写插件。

问题

游戏服务器开发的时候,会把数据放入protobuf结构中进行序列化,然后放入key-value db中,从db加载出来后再反序列化成一个protobuf对象,有时候我们可能不能直接使用protobuf对象,而是需要转换成一个C struct(如需要放入共享内存的时候)。这时候我们就需要一种很方便把protobuf定义的message可以映射成一个C结构,同时又能从一个C结构还原成protobuf的message的方式。每次都逐个字段的去写,即繁琐又容易出错,一般这种能让工具生成是最好的。

protobuf拓展

我们需要指定哪些message需要生成c struct,哪些不需要,因此我们可以定义message的拓展。 新建一个文件,假设叫做 pod_options.proto

import "google/protobuf/descriptor.proto";extend google.protobuf.MessageOptions{ // 1: need to generate pod data code // 2: need to generate pod data code without constructor optional int32 gen_pod = 30000;}

接下来,可以定义我们自己的test.proto了。

import "pod_options.proto";message NeedPod{ // 设置了1,需要生成 c struct option (gen_pod) = 1; optional uint32 id = 1;}message DontNeedPod{ optional uint32 id = 1;}

解析proto文件

有了拓展字段,接下来怎么获取呢,难道要修改protoc的源码,输出需要的c struct么? 其实,并不需要修改源码,起码有几种方法可以达到目的。

使用Importer

仔细阅读protoc源码,有一个 google::protobuf::compiler::Importer 类

// Simple interface for parsing .proto files. This wraps the process// of opening the file, parsing it with a Parser, recursively parsing all its// imports, and then cross-linking the results to produce a FileDescriptor. This is really just a thin wrapper around SourceTreeDescriptorDatabase.// You may find that SourceTreeDescriptorDatabase is more flexible. TODO(kenton): I feel like this class is not well-named.class PROTOBUF_EXPORT Importer {

public:

Importer(SourceTree* source_tree, MultiFileErrorCollector* error_collector);

// Import the given file and build a FileDescriptor representing it. If // the file is already in the DescriptorPool, the existing FileDescriptor // will be returned. The FileDescriptor is property of the DescriptorPool, // and will remain valid until it is destroyed. If any errors occur, they // will be reported using the error collector and Import() will return NULL. // // A particular Importer object will only report errors for a particular // file once. All future attempts to import the same file will return NULL // without reporting any errors. The idea is that you might want to import // a lot of files without seeing the same errors over and over again. If // you want to see errors for the same files repeatedly, you can use a // separate Importer object to import each one (but use the same // DescriptorPool so that they can be cross-linked). const FileDescriptor* Import(const std::string& filename);

//………………}

早期的protobuf版本就是用这个类来产生FileDescriptor,而有了FileDescriptor,就有了整个proto文件的所有信息。Importer的用法可以参照早期版本的protoc源码,或者在单元测试用例中也仍然有用到Importer,大概代码如下:

MultiFileErrorCollector error_collector;

DiskSourceTree source_tree;

// MapPath的用法可以查看源码的注释,这里就不细说了source_tree.MapPath("", "./");

Importer importer(&source_tree, &error_collector);

const FileDescriptor* parsed_descriptor = importer.Import("test.proto");

使用py2文件

还有一种方法可以以曲线救国的方式来实现我们的目的。我们知道protoc可以支持产出不同语言的目标文件,而针对python的目标文件就是py2.py文件,查看 py2 文件,可以看到,里面已经包含了FileDescriptor类了,因此,只要用python写一个脚本加载这个py2文件,就可以获得一个FileDescriptor类

protobuf插件

相比于前面的两种方法,protobuf官方支持自定义插件,只要根据protobuf要求的方式编写插件,大概的格式如下:

#include #include #include #include #include using namespace google::protobuf::compiler;

class PodCodeGenerator : public CodeGenerator

{

public:

virtual ~PodCodeGenerator(){};

virtual bool Generate(const ::google::protobuf::FileDescriptor* file_, const string& parameter, GeneratorContext* context, string* error) const

{

// 根据FileDescriptor,产生输出文件的信息 return true;

}

};

int main(int argc, char* argv[])

{

PodCodeGenerator generator;

return google::protobuf::compiler::PluginMain(argc, argv, &generator);

}

可以看到,Generate函数的参数里面已经有protobuf解析好的FileDescriptor了,接下来就是根据自己需要的格式生成了。至于调用插件的方式也是很简单,假设插件的二进制文件叫test_plugin,则如下:protoc --proto_path=your path --NAME_out=output path --plugin=protoc-gen-NAME=test_plugin test.proto

其中的NAME可以是自定义的字符串,另外还可以加上 -NAME_opt=your param 选项带上参数给插件。使用插件的方式可以很好的在单次解析中完成pb和自定义格式的输出。

P.S. 插件的示例源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值