一、目的
介绍ProtoBuf插件原理,并实践用C++和Golang实现
二、原理
这里以2.6.1为例,查看protobuf-2.6.1/src/google/protobuf/compiler/main.cc代码,默认注册cpp\java\python 3个generator。最后调用cli.Run接口执行。
int main(int argc, char* argv[]) {
google::protobuf::compiler::CommandLineInterface cli;
cli.AllowPlugins("protoc-");
// Proto2 C++
google::protobuf::compiler::cpp::CppGenerator cpp_generator;
cli.RegisterGenerator("--cpp_out", "--cpp_opt", &cpp_generator,
"Generate C++ header and source.");
// Proto2 Java
google::protobuf::compiler::java::JavaGenerator java_generator;
cli.RegisterGenerator("--java_out", &java_generator,
"Generate Java source file.");
// Proto2 Python
google::protobuf::compiler::python::Generator py_generator;
cli.RegisterGenerator("--python_out", &py_generator,
"Generate Python source file.");
return cli.Run(argc, argv);
}
需要关注的是每种语言的生成器都继承自CodeGenerator。
三、
1. 场景
定义一个proto文件,实现不同的插件功能。我们会在golang实践中实现protobuf导出支持rpc的接口,其中proto文件如下所示:
syntax = "proto3";
package comm;
message String {
string value = 1;
}
service HelloService {
rpc Hello (String) returns (String);
}
2. 实践-Golang
首先我们来看下protoc-gen-go的源码,位置在$(GOPATH)/src/github.com/golang/protobuf/protoc-gen-go下。
package main
import (
"io/ioutil"
"os"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
func main() {
// Begin by allocating a generator. The request and response structures are stored there
// so we can do error handling easily - the response structure contains the field to
// report failure.
g := generator.New()
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
g.Error(err, "reading input")
}
if err := proto.Unmarshal(data, g.Request); err != nil {
g.Error(err, "parsing input proto")
}
if len(g.Request.FileToGenerate) == 0 {
g.Fail("no files to generate")
}
g.CommandLineParameters(g.Request.GetParameter())
// Create a wrapped version o