拒做PB Boy!教你巧用 Protobuf 反射来优化代码( 三 )

写重复字段的函数如下:
void SetRepeatedInt32(Message * message, const FieldDescriptor * field, int index, int32 value) constvoid SetRepeatedString(Message * message, const FieldDescriptor * field, int index, std::string value) constvoid SetRepeatedEnumValue(Message * message, const FieldDescriptor * field, int index, int value) const // Set an enum field's value with an integer rather than EnumValueDescriptor. more..新增重复字段设计如下:
void AddInt32(Message * message, const FieldDescriptor * field, int32 value) constvoid AddString(Message * message, const FieldDescriptor * field, std::string value) const另外有一个较为重要的函数,其可以批量获取字段描述并将其放置到 vector 中:
void Reflection::ListFields(const Message & message, std::vector< const FieldDescriptor * > * output) const2.4 options 介绍PB 允许在 proto 中自定义选项并使用选项 。在定义 message 的字段时,不仅可以定义字段内容,还可以设置字段的属性,比如校验规则,简介等,结合反射,可以实现丰富丰富多彩的应用 。
下面来介绍下:
import "google/protobuf/descriptor.proto";extend google.protobuf.FieldOptions {  optional uint32 attr_id              = 50000; //字段id  optional bool is_need_encrypt        = 50001 [default = false]; // 字段是否加密,0代表不加密,1代表加密  optional string naming_conventions1  = 50002; // 商户组命名规范  optional uint32 length_min           = 50003  [default = 0]; // 字段最小长度  optional uint32 length_max           = 50004  [default = 1024]; // 字段最大长度  optional string regex                = 50005; // 该字段的正则表达式}message SubMerchantInfo {  // 商户名称  optional string merchant_name = 1 [    (attr_id) = 1,    (is_encrypt) = 0,    (naming_conventions1) = "company_name",    (length_min) = 1,    (length_max) = 80,    (regex.field_rules) = "[a-zA-Z0-9]"  ];使用方法如下:
#include <google/protobuf/descriptor.h>#include <google/protobuf/message.h>std::string strRegex = FieldDescriptor->options().GetExtension(regex);uint32 dwLengthMinp = FieldDescriptor->options().GetExtension(length_min);bool bIsNeedEncrypt = FieldDescriptor->options().GetExtension(is_need_encrypt);三、PB 反射的进阶使用第二章给出了 PB 反射,以及具体的使用细节,在本章中,作者结合自己日常的代码,给出 PB 反射一些使用场景 。并且以开发一个表单系统为例,讲一下 PB 反射在开发表单系统中的进阶使用 。
3.1 获取 PB 中所有非空字段在业务中,经常会需要获取某个 Message 中所有非空字段,形成一个 map<string,string>,使用 PB 反射写法如下:
#include "pb_util.h"#include <sstream>namespace comm_tools {int PbToMap(const google::protobuf::Message &message,            std::map<std::string, std::string> &out) {#define CASE_FIELD_TYPE(cpptype, method, valuetype)                              case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: {                     valuetype value = reflection->Get##method(message, field);                     std::ostringstream oss;                                                        oss << value;                                                                  out[field->name()] = oss.str();                                                break;                                                                       }#define CASE_FIELD_TYPE_ENUM()                                                   case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {                          int value = reflection->GetEnum(message, field)->number();                     std::ostringstream oss;                                                        oss << value;                                                                  out[field->name()] = oss.str();                                                break;                                                                       }#define CASE_FIELD_TYPE_STRING()                                                 case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {                        std::string value = reflection->GetString(message, field);                     out[field->name()] = value;                                                    break;                                                                       }  const google::protobuf::Descriptor *descriptor = message.GetDescriptor();  const google::protobuf::Reflection *reflection = message.GetReflection();  for (int i = 0; i < descriptor->field_count(); i++) {    const google::protobuf::FieldDescriptor *field = descriptor->field(i);    bool has_field = reflection->HasField(message, field);    if (has_field) {      if (field->is_repeated()) {        return -1; // 不支持转换repeated字段      }      const std::string &field_name = field->name();      switch (field->cpp_type()) {        CASE_FIELD_TYPE(INT32, Int32, int);        CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);        CASE_FIELD_TYPE(FLOAT, Float, float);        CASE_FIELD_TYPE(DOUBLE, Double, double);        CASE_FIELD_TYPE(BOOL, Bool, bool);        CASE_FIELD_TYPE(INT64, Int64, int64_t);        CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);        CASE_FIELD_TYPE_ENUM();        CASE_FIELD_TYPE_STRING();      default:        return -1; // 其他异常类型      }    }  }  return 0;}} // namespace comm_tools


推荐阅读