ProtoBuffer 自定义 option 扩展

相信使用过 ProtoBuffer 的同学都不陌生以下的定义, 一个 Kid ProtoBuffer 对象,有一个 age 字段,且默认值是7。

1
2
3
4
5
message Kid
{
optional uint32 age = 1 [default=7];
optional uint32 score = 2 ;
}

运用 PB 的 默认 option 扩展,我们可以指定一些行为,比如上面的默认初始值。

我们想实现自定义的选项,比如,我想指定一种上报的 option, 对于此类 option, 我就将这种字段上报给老师。那么定义的 ProtoBuffer 大概会长这样:

1
2
3
4
5
message Kid
{
optional uint32 age = 1 [default=7];
optional uint32 score = 2 [action = Report];
}

是否可行呢?幸运的是 PB 有自定义 option 的机制,使得我们的设想变成可能。

如何实现自定义扩展

1: 注册 Fieldoption , action.proto 就是定义好了 option 的 Proto 文件

1
2
3
4
5
6
7
8
9
package action;
import "google/protobuf/descriptor.proto";

enum ActionType {
REPORT = 1;
}
extend google.protobuf.Fieldoptions {
optional ActionType rule = 12345 [default = REPORT];
}

2: 应用 option
import 定义的 Proto 文件, 就可以轻易用上了。

1
2
3
4
5
6
7
package Kid                                                                          
import "action.proto";
message person
{
optional uint32 age = 1;
optional int32 age = 2 [(action.rule) = REPORT];
}

怎么通过反射获取 option 的值呢

通过 descriptor && reflection 我们可以得到 option 的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include "kid.pb.h"
using namespace std;

int main()
{
Kid obj;
obj.set_age(12);

auto descriptor = obj.GetDescriptor();
auto reflection = obj.GetReflection();
// 通过 FieldName 获取字段
auto idField = descriptor->FindFieldByName("age");
reflection->SetInt32(&obj, idField, 18);
for (unsigned int i = 0; i < descriptor->field_count(); ++i)
{
auto field = descriptor->field(i);

cout << "field_is_repeated:" << field->is_repeated() << endl;
// 获取 field_num
cout << "field_num:" << field->number() << endl;
// 获取 option cout << "optiona value:" << field->options().GetExtension(action::rule) << endl;

switch (field->type())
{
case ::google::protobuf::FieldDescriptor::TYPE_INT32:
cout << "int type, value:" << reflection->GetInt32(obj, field);
break;
case ::google::protobuf::FieldDescriptor::TYPE_FLOAT:
cout << "float type, value:" << reflection->GetFloat(obj, field);
break;
default:
cout << "not found" << endl;
}
}
return 0;
}

以上的例子,通过反射我们也可以在不知道字段的名字情况下,得到它的值和类型等信息。