背景
在项目开发中,由于需要使用到一个protobuf的API, 用于对不同Message的比较过程中,设置浮点数比较精度的API, MessageDifferencer::SetFractionAndMargin(const FieldDescriptor * field, double fraction, double margin),需要传入一个Message的FieldDescriptor,而项目中想对所有的浮点型的Field都设置默认精度,那么,就有了遍历一个Message中的所有FieldDescriptor的需求出来了。
实现方案
直接想在网上找代码片断,可惜了,百度和谷歌都没有谁有现成的代码出来,那就只能自己阅读API文档,做第一个贡献者吧。
自己调试了几个版本,终于算是把已知的一些问题都修复了,不排除还有一些未知的BUG,请大家指正。
主要使用的几个API,以及需要注意的问题。
1、如何获取当前Message的所有FieldDescriptor。
A)对于一个Message获取其下层的所有Field,可以通过Descriptor*获取,这个获取的是协议定义中的所有(无论是否被设置)
B)通过Relection中的ListFields去获取,这个是获取所有已设置的。(应该使用这个API)
2、对于Repeated的Field。通过Reflection的FieldSize去获取其Field的个数。
A)对于其类型是CPPTYPE_MESSAGE,需要进行递归遍历。
B)对于非CPPTYPE_MESSAGE,则直接返回,没有子层级的Field了。
这里直接上最终的实现代码,有类似需求的可以参考一下
遍历FieldDescriptor
/*
* 获取一个message的所有层级的FieldDescriptor
*/
static
bool
TraversalFieldDescript(
const
Message& message, std::vector<
const
FieldDescriptor*>& v_field_dpt) {
const
Descriptor* descriptor = message.GetDescriptor();
const
Reflection* reflection = message.GetReflection();
if
(NULL == descriptor) {
LOG_ERR(
"descriptor is null"
);
return
false
;
}
v_field_dpt.clear();
DoTraversalFieldDescript(message, descriptor, v_field_dpt);
return
true
;
}
static
void
DoTraversalFieldDescript(
const
Message& message,
const
Descriptor* descriptor,
std::vector<
const
FieldDescriptor*>& v_field_dpt) {
const
Reflection* reflection = message.GetReflection();
if
(NULL == reflection) {
LOG_ERR(
"reflection is null"
);
return
;
}
if
(NULL == descriptor) {
return
;
}
const
FieldDescriptor* field = NULL;
vector<
const
FieldDescriptor*> v_field_list;
reflection->ListFields(message, &v_field_list);
for
(
size_t
i = 0; i < v_field_list.size(); ++i) {
field = v_field_list[i];
if
(NULL == field) {
LOG_ERR(
"field is null"
);
return
;
}
LOG_DBG(
"field_name [%s] set"
, field->full_name().c_str());
//非复合类型,加到数组中返回
if
(FieldDescriptor::CPPTYPE_MESSAGE != field->cpp_type() &&
!field->is_repeated()) {
v_field_dpt.push_back(field);
continue
;
}
// CPPTYPE_MESSAGE
if
(!field->is_repeated()) {
//not repeated
const
Message& current_msg = reflection->GetMessage(message, field);
v_field_dpt.push_back(field);
const
Descriptor* sub_dst = field->message_type();
DoTraversalFieldDescript(current_msg, sub_dst, v_field_dpt);
}
else
{
int
count = reflection->FieldSize(message, field);
v_field_dpt.push_back(field);
if
(FieldDescriptor::CPPTYPE_MESSAGE != field->cpp_type()) {
continue
;
}
//repeated and CPPTYPE_MESSAGE
for
(
int
j = 0; j < count; ++j) {
const
Message& current_msg = reflection->GetRepeatedMessage(message, field, j);
const
Descriptor* sub_dst = field->message_type();
DoTraversalFieldDescript(current_msg, sub_dst, v_field_dpt);
}
}
}
}
|