ProtoBuf(Google Protocol Buffers)—— 反射原理以及反射具体流程介绍

ProtoBuf—— 反射原理

1、反射原理

1.1、反射机制的背景

有关某个外部世界表示”的计算过程, 并通过它来对那个外部世界进行推理; 那么我们也可以构造能够对自身表示和计算进行推理的计算过程,它包含负责管理有关自身的操作和结构表示的内部过程。
—— 1982年 Smith, Brian Cantwell 博士论文首次提出

  • 所谓编程实际上就是在构造 “关于外部世界” 的计算过程。如果用 F 表示这个构造过程,用 X表示外部世界,那么编写一个计算系统可表示为 F(X)
  • 我们完全可以构造对上述过程本身进行描述和推理的计算过程。即将 F(X) 视为新的 “世界” 和 研究对象,构造 F(F(X))
    • 所以我们为了构建和改造世界,前提需要充分认识世界

计算机程序在运行时可以访问、检测和修改它本身状态或行为
—— 反射 (计算机科学) Wikipedia

我们平时编写的计算系统是面向特定领域的(通常是面向现实建模), 系统中包含 用来描述领域中的实体实体间关系 的数据结构以及处理这些数据结构的规则。那么反射系统面向领域便是这个系统本身。

1.2、定义

在对程序本身进行构建的同时,在运行时可以访问和修改自身状态和行为,这建立在对系统系统元信息充分感知的基础上。

系统元信息:

元信息:即系统自描述信息,用于描述系统本身。举例来讲,即系统有哪些类?类中有哪些字段、哪些方法?字段属于什么类型、方法又有怎样的参数和返回值?

反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

1.3、反射原理关注的一些问题

  • 1、如何在程序运行过程中通过类型名字(一个字符串,合法但是内容在编译期间未知,比如是在配置文件中获取的)创建出类型对象.
  • 2、如何在程序运行过程中通过对象和对象的属性的名字(一个字符串,合法但是内容在编译期间未知,比如是通过通讯包获取的)获取,修改对应属性.
  • 3、如何在程序运行过程中通过对象和对象方法的名字(一个字符串,合法但是内容在编译期间未知,比如是从用户输入获取的)调用对应的方法.

1.4、反射原理的优势和应用

优势:
反射在运行时检测或修改程序行为的程序中,提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类.
应用:

  • 1、多并发的服务器框架
  • 2、网络序列化数据传输方法等
  • 3、微软的MFC

在开发编辑器的阶段,并没有相应的类,但是使用者想通过在编辑器中传入类的名字,然后在开发中,根据编辑器传入的类名,新建一个类去实现的话,这种方法非常适用。

2、ProtoBuf反射原理——获取并改造 元信息

2.1 、 .proto 文件

ProtoBuf 反射所需的元信息在哪?答案便是使用 ProtoBuf 的第一步就会接触到的:.proto 文件。

  • .proto 文件
    • 使用 ProtoBuf 内置的工具 protoc 编译器编译,protoc 将 .proto 文件内容编码并写入生成的代码中(.pb.cc文件)
    • 使用 ProtoBuf 提供的编译 API 在运行时手动(指编码)解析 .proto 文件内容。实际上 protoc底层调用的也正是这个编译 API。
// 在 xxx.proto 文件中定义 Example1 message
message Example1 {
    optional string stringVal = 1;
    optional bytes bytesVal = 2;
    message EmbeddedMessage {
        int32 int32Val = 1;
        string stringVal = 2;
    }
    optional EmbeddedMessage embeddedExample1 = 3;
    repeated int32 repeatedInt32Val = 4;
    repeated string repeatedStringVal = 5;
}
//提供我们数据元信息的过程,这些元信息包括数据由哪些字段构成,字段又属于什么类型以及字段之间的组合关系等

2.2 、 反射原理过程

无论 .proto 文件来源于何处,我们都需要对其做进一步的处理,将其解析成内存对象,并构建其与实例的映射,同时也要计算每个字段的内存偏移:

  • 1、提供 .proto (范指 ProtoBuf Message 语法描述的元信息)
  • 2、解析 .proto 构建 FileDescriptor、FieldDescriptor 等,即 .proto 对应的内存模型(对象)
  • 3、之后每创建一个实例,就将其存到相应的实例池中
  • 4、将 Descriptorinstance 的映射维护到表中备查
  • 5、通过 Descriptor 可查到相应的 instance,又由于了解 instance中字段类型(FieldDescriptor),所以知道字段的内存偏移,那么就可以访问或修改字段的值
    • 任何一个对象最终都对应一段内存,有内存起始(start_addr)和结束地址, 而对象的每一个属性,都位于start_addr+$offset ,所以当对象和对应属性的offset已知的时候, 属性的内存地址也就是可以获取的。

2.3 、 反射相关的类和API

  • 1、google::protobuf::Message
  • 2、google::protobuf::Descriptor
  • 3、google::protobuf::Reflection
  • 4、google::protobuf::FieldDescriptor
    在这里插入图片描述
    Person是自定义的protobuf类型
2.3.1、google::protobuf::Message
  • Person是自定义的pb类型,继承自Message.
  • MessageLite作为Message基类,更加轻量级一些。
  • 通过Message的两个接口GetDescriptor/GetReflection,可以获取该类型对应的Descriptor/Reflection。
//google::protobuf::Message
const Descriptor* GetDescriptor() const;
const Reflection* GetReflection() const;
2.3.2、 google::protobuf::Descriptor

Descriptor是对message类型定义的描述,包括message的名字、所有字段的描述、原始的proto文件内容等。

//Descriptor
//返回message的属性个数
int field_count() const;
//返回第index个属性
const FieldDescriptor* field(int index) const;
//通过proto文件里面定义的tag, 返回属性
const FieldDescriptor* FindFieldByNumber(int number) const;
//通过属性名,返回属性
const FieldDescriptor* FindFieldByName(const std::string& name) const;
//通过小写的属性名,返回属性
const FieldDescriptor* FindFieldByLowercaseName(
      const std::string& lowercase_name) const;
//通过驼峰属性名,返回属性
const FieldDescriptor* FindFieldByCamelcaseName(
      const std::string& camelcase_name) const;
//返回枚举类型的数量
int enum_type_count() const;
//返回第index个枚举类型属性
const EnumDescriptor* enum_type(int index) const;
//通过名称,返回枚举属性
const EnumDescriptor* FindEnumTypeByName(const std::string& name) const;
//通过属性值,返回枚举值属性
const EnumValueDescriptor* FindEnumValueByName(const std::string& name) const;

2.3.3、 google::protobuf::Reflection
  • Reflection:接口类,主要提供了动态读写pb字段的接口,对pb对象的自动读写主要通过该类完成。
  • 提供方法来动态访问/修改message中的field的接口类,对每种类型,Reflection都提供了一个单独的接口用于读写字段对应的值。
//检查message中的非repeated属性是否存在
bool HasField(const Message& message, const FieldDescriptor* field) const;
//返回message中repeated属性的长度
int FieldSize(const Message& message, const FieldDescriptor* field) const;
//清除message中的属性,非repeated属性HashField返回false,repeated属性FieldSize返回0
void ClearField(Message* message, const FieldDescriptor* field) const;
//返回message中除Unknown的属性,包括所有HashField返回为true,FieldSize非0的属性
void ListFields(const Message& message,
                  std::vector<const FieldDescriptor*>* output) const;
//返回message对应属性的,对应类型的返回值
int32 GetInt32(const Message& message, const FieldDescriptor* field) const;
  int64 GetInt64(const Message& message, const FieldDescriptor* field) const;
  uint32 GetUInt32(const Message& message, const FieldDescriptor* field) const;
  uint64 GetUInt64(const Message& message, const FieldDescriptor* field) const;
  float GetFloat(const Message& message, const FieldDescriptor* field) const;
  double GetDouble(const Message& message, const FieldDescriptor* field) const;
  bool GetBool(const Message& message, const FieldDescriptor* field) const;
  std::string GetString(const Message& message,
                        const FieldDescriptor* field) const;
  const EnumValueDescriptor* GetEnum(const Message& message,
                                     const FieldDescriptor* field) const;

//返回message,属性的value的int类型值
int GetEnumValue(const Message& message, const FieldDescriptor* field) const;
const Message& GetMessage(const Message& message,
                            const FieldDescriptor* field,
                            MessageFactory* factory = nullptr) const;

//设置message,对应属性的对应类型的值
void SetInt32(Message* message, const FieldDescriptor* field,
                int32 value) const;
  void SetInt64(Message* message, const FieldDescriptor* field,
                int64 value) const;
  void SetUInt32(Message* message, const FieldDescriptor* field,
                 uint32 value) const;
  void SetUInt64(Message* message, const FieldDescriptor* field,
                 uint64 value) const;
  void SetFloat(Message* message, const FieldDescriptor* field,
                float value) const;
  void SetDouble(Message* message, const FieldDescriptor* field,
                 double value) const;
  void SetBool(Message* message, const FieldDescriptor* field,
               bool value) const;
  void SetString(Message* message, const FieldDescriptor* field,
                 const std::string& value) const;
  void SetEnum(Message* message, const FieldDescriptor* field,
               const EnumValueDescriptor* value) const;
  void SetEnumValue(Message* message, const FieldDescriptor* field,
                    int value) const;
  Message* MutableMessage(Message* message, const FieldDescriptor* field,
                          MessageFactory* factory = nullptr) const;

//返回message对应属性repeated字段的index位置的值
int32 GetRepeatedInt32(const Message& message, const FieldDescriptor* field,
                         int index) const;
  int64 GetRepeatedInt64(const Message& message, const FieldDescriptor* field,
                         int index) const;
  uint32 GetRepeatedUInt32(const Message& message, const FieldDescriptor* field,
                           int index) const;
  uint64 GetRepeatedUInt64(const Message& message, const FieldDescriptor* field,
                           int index) const;
  float GetRepeatedFloat(const Message& message, const FieldDescriptor* field,
                         int index) const;
  double GetRepeatedDouble(const Message& message, const FieldDescriptor* field,
                           int index) const;
  bool GetRepeatedBool(const Message& message, const FieldDescriptor* field,
                       int index) const;
  std::string GetRepeatedString(const Message& message,
                                const FieldDescriptor* field, int index) const;
  const EnumValueDescriptor* GetRepeatedEnum(const Message& message,
                                             const FieldDescriptor* field,
                                             int index) const;
  int GetRepeatedEnumValue(const Message& message, const FieldDescriptor* field,
                           int index) const;
  const Message& GetRepeatedMessage(const Message& message,
                                    const FieldDescriptor* field,
                                    int index) const;
//设置message的repeated属性的index位置对应类型的value的值
void SetRepeatedInt32(Message* message, const FieldDescriptor* field,
                        int index, int32 value) const;
  void SetRepeatedInt64(Message* message, const FieldDescriptor* field,
                        int index, int64 value) const;
  void SetRepeatedUInt32(Message* message, const FieldDescriptor* field,
                         int index, uint32 value) const;
  void SetRepeatedUInt64(Message* message, const FieldDescriptor* field,
                         int index, uint64 value) const;
  void SetRepeatedFloat(Message* message, const FieldDescriptor* field,
                        int index, float value) const;
  void SetRepeatedDouble(Message* message, const FieldDescriptor* field,
                         int index, double value) const;
  void SetRepeatedBool(Message* message, const FieldDescriptor* field,
                       int index, bool value) const;
  void SetRepeatedString(Message* message, const FieldDescriptor* field,
                         int index, const std::string& value) const;
  void SetRepeatedEnum(Message* message, const FieldDescriptor* field,
                       int index, const EnumValueDescriptor* value) const;
  void SetRepeatedEnumValue(Message* message, const FieldDescriptor* field,
                            int index, int value) const;
  Message* MutableRepeatedMessage(Message* message,
                                  const FieldDescriptor* field,
                                  int index) const;

2.3.4、 google::protobuf::FieldDescriptor

FieldDescriptor描述message中的单个字段,例如字段名,字段属(optional/required/repeated)等。对于proto定义里的每种类型,都有一种对应的C++类型,

  //是否是必须的
  bool is_required() const;  // shorthand for label() == LABEL_REQUIRED
  //是否是可选的
  bool is_optional() const;  // shorthand for label() == LABEL_OPTIONAL
  //是否是repeated
  bool is_repeated() const;  // shorthand for label() == LABEL_REPEATED
  //是否可以pack
  bool is_packable() const;  // shorthand for is_repeated() &&
                             //               IsTypePackable(type())
  //是否是packed
  bool is_packed() const;    // shorthand for is_packable() &&
                             //               options().packed()
  //是否是message
  bool is_map() const;       // shorthand for type() == TYPE_MESSAGE &&
                             // message_type()->options().map_entry()
 //属性在message的索引位置
 int index() const;

2.3.5、 其他相关接口
2.3.5.1、DescriptorPool(元信息池)
  • 任何时候想要查询一个Descriptor , 都是去DescriptorPool里面查询。
  • 缓存所有查询的文件的Descriptor 。对外提供了诸如 FindServiceByNameFindMessageTypeByName等各类接口以便外部查询所需的元信息。
  • 当 DescriptorPool 不存在时需要查询的元信息时,将进一步到 DescriptorDatabase 中去查找。
2.3.5.2、DescriptorDatabase( .proto 文件库)
  • DescriptorDatabase是一个纯虚基类,描述了一系列符合通过名字(文件名,符号名。。。)
    来获取FileDescriptorProto的接口
  • 可从硬编码或磁盘中查询对应名称的 .proto 文件内容,解析后返回查询需要的元信息。
// 这里我干掉了里面的英文注释.
class LIBPROTOBUF_EXPORT DescriptorDatabase {
    public:
        inline DescriptorDatabase() {}
        virtual ~DescriptorDatabase();
        virtual ~DescriptorDatabase();
        // 通过文件名字找.
        virtual bool FindFileByName(const string& filename,
                FileDescriptorProto* output) = 0;
        // 通过符号名字找.
        virtual bool FindFileContainingSymbol(const string& symbol_name,
                FileDescriptorProto* output) = 0;
        // 通过扩展信息找.
        virtual bool FindFileContainingExtension(const string& containing_type,
                int field_number,
                FileDescriptorProto* output) = 0;
        // 通过扩展信息的tag数字找...
        virtual bool FindAllExtensionNumbers(const string& /* extendee_type */,
                vector<int>* /* output */) {
            return false;
        }
    private:
        GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase);
};

核心的两个派生类是 :

  • EncodedDescriptorDatabase
    • 支持DescriptorDatabase的全部接口
    • 接收序列化之后的FileDescriptorProto, 保存在map中备查.
    • 这个类对应着预先编译链接好的那些类型的反射机制。
  • SourceTreeDescriptorDatabase
    • 仅支持DescriptorDatabaseFindFileByName接口。其余直接返回false.
    • 每次查询某个文件都是从磁盘读入proto的源文件,编译解析后返回对应的FileDescriptorProto .
    • 这个类对应着动态编译proto源文件的时候的反射机制.
2.3.5.3、MessageFactory( 实例工厂)

任何时候想要获取一个类型的instance , 都要去MessageFactory里面获取。
MessageFactory 是一个纯虚的基类,定义了通过Descripor来获取对应类型instance的接口.

{
 public:
  inline MessageFactory() {}
  virtual ~MessageFactory();
  // 了通过Descripor来获取对应类型instance 的接口
  virtual const Message* GetPrototype(const Descriptor* type) = 0;

  // 这个是获取编译链接好的那些类型的factory单例的入口.
  static MessageFactory* generated_factory();
  // 这个是对应的像上面哪个单例内填装数据的接口,protoc自动生成的文件都会有调用.
  static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
                                               const Message* prototype);
 private:
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory);
}

同样有两个核心的派生类

  • GeneratedMessageFactory
    • 一个map , 保存着DescriptorMessage
    • 这个类对应着预先编译链接好的那些类型的反射机制。
  • DynamicMessageFactory
    • 有简单的缓存,保存自己解析过的Descriptor`` < /li >
    • < li >可以通过Descriptor,动态的基于内存构造出一个Message

2.4 、 反射原理过程具体解析

/* 反射创建实例 */
auto descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("Dog");
auto prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
auto instance = prototype->New();

/* 反射相关接口 */
auto reflecter = instance.GetReflection();
auto field = descriptor->FindFieldByName("name");
reflecter->SetString(&instance, field, "鸡你太美") ;

// 获取属性的值.
std::cout<<reflecter->GetString(instance , field)<< std::endl ;
return 0 ;
2.4.1、获取元信息
  • 数据存储在哪里
    • 1、所有的Descriptor存储在单例的DescriptorPool 中。
      • google::protobuf::DescriptorPool::generated_pool()来获取他的指针。
    • 2、所有的instance 存储在单例的MessageFactory中。
      • google::protobuf::MessageFactory::generated_factory()来获取他的指针。
    • 3、将所有的Descriptor & instance 提前维护到表中备查

DescriptorPool 相当于缓存了文件的 Descriptor(底层使用 Map),查询时将先到缓存中查询,如果未能找到再进一步到 DB 中(即 DescriptorDatabase)查询,此时可能需要从磁盘中读取文件内容,然后再解析成 Descriptor 返回,这里需要消耗一定的时间。

不难看出,DescriptorPoolDescriptorDatabase 通过缓存机制提高了反射运行效率,但这只是反射工程实现上的一种优化,我们更感兴趣的应该是 Descriptor 的来源。

DescriptorDatabase 从磁盘中读取 .proto 内容并解析成 Descriptor 这一来源很容易理解,但我们大多数时候并不会采用这种方式,反射时也不会去读取 .proto 文件。那么我们的 .proto 内容在哪?

实际上我们在使用 protoc 生成 xxx.pb.cc 和 xxx.pb.h 文件时,其中不仅仅包含了读写数据的接口,还包含了 .proto 文件内容。阅读任意一个 xxx.pb.cc 的内容,你可以看到如下类似代码

static void AddDescriptorsImpl() {
  InitDefaults();

  // .proto 内容
  static const char descriptor[] GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {
      "\n\022single_int32.proto\"\035\n\010Example1\022\021\n\010int3"
      "2Val\030\232\005 \001(\005\" \n\010Example2\022\024\n\010int32Val\030\377\377\377\377"
      "\001 \003(\005b\006proto3"
  };

  // 注册 descriptor
  ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
      descriptor, 93);

  // 注册 instance
  ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
    "single_int32.proto", &protobuf_RegisterTypes);
}

其中 descriptor 数组存储的便是 .proto 内容。这里当然不是简单的存储原始文本字符串,而是经过了 SerializeToString 序列化处理,而后将结果以硬编码的形式保存在 xxx.pb.cc

硬编码的 .proto 元信息内容将以懒加载(类似于单例模式中的懒汉模式)的方式(被调用时才触发)被 DescriptorDatabase 加载、解析,并缓存到 DescriptorPool 中。

2.4.2、创建和获取实例(查表)

根据 MessageFactory 获得了一个实例。MessageFactory 是实例工厂,对外提供了根据元信息 descriptor 获取相应实例的能力。

// 注册对应 descriptor 的 instance 到 MessageFactory
// InternalRegisterGeneratedFile 函数内部,会将创建一个实例并做好 descriptor 与 instance 的映射
// xxx 应该替换为文件名,根据自己的需求取`在这里插入代码片`
namespace {

//! 将本文件内的全部类型的instance注册进入MessageFactory的接口.
void protobuf_RegisterTypes(const ::std::string&) {
   // 初始化本文件的reflection数据.
  protobuf_AssignDescriptorsOnce();
  ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
    Test_descriptor_, &Test::default_instance());
}
//! 本文件的初始接口.
void protobuf_AddDesc_xxx_2eproto() {
  static bool already_here = false;
  if (already_here) return;
  already_here = true;
  GOOGLE_PROTOBUF_VERIFY_VERSION;
  // 注册本文件的Descriptor包. 这样就可以用名字通过generated_pool获取对应的Descriptor。
  ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
    "\n\013xxx.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);
  // 将本文件的类型instance注册接口注册给MessageFactory.
  // 这里注册接口是为了实现类型的lazy注册。如果没有使用请求某个文件的类型,就不注册对应文件的类型。
  ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
    "xxx.proto", &protobuf_RegisterTypes);
  // 构造并且初始化全部instance.
  Test::default_instance_ = new Test();
  Test::default_instance_->InitAsDefaultInstance();
  // 注册清理接口.
  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_xxx_2eproto);
}
//! 下面利用全局变量的构造函数确保main函数执行之前数据已经进行注册.
struct StaticDescriptorInitializer_xxx_2eproto {
  StaticDescriptorInitializer_xxx_2eproto() {
    protobuf_AddDesc_xxx_2eproto();
  }
} static_descriptor_initializer_xxx_2eproto_;
}

每次构建实例后,都将 descriptor 和 instance 维护到一个 _table 中,即映射表以便获取。后续所谓通过反射获得某个类的某个实例子,实际就是查表的过程。

2.4.3、实例对象的读写

实例对象的 reflection 里面存储了对象属性的偏移地址,而这些信息其实与 .proto 内容信息一样,在 protoc 编译时通过解析 proto 文件内容获得且记录在 xxx.pb.cc 中。

有了属性的内存偏移,自然可以对属性进行读写操作

  • 任何一个对象最终都对应一段内存,有内存起始(start_addr)和结束地址, 而对象的每一个属性,都位于start_addr+$offset ,所以当对象和对应属性的offset已知的时候, 属性的内存地址也就是可以获取的。
  • GeneratedMessageReflection 的填装和获取
    对于每一个message , 都有一个对应的GeneratedMessageReflection 对象.
    这个对象保存了对应message反射操作需要的信息.

//!初始化本文件的所有GeneratedMessageReflection对象.
void protobuf_AssignDesc_xxx_2eproto() {
  protobuf_AddDesc_xxx_2eproto();
  const ::google::protobuf::FileDescriptor* file =
    ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
      "xxx.proto");
  GOOGLE_CHECK(file != NULL);
  Test_descriptor_ = file->message_type(0);
  static const int Test_offsets_[1] = {
    //这里在计算属性的内存偏移.
    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, id_),
  };
  // 这里是个test包填装的GeneratedMessageReflection对象.
  Test_reflection_ =
    new ::google::protobuf::internal::GeneratedMessageReflection(
      Test_descriptor_,
      Test::default_instance_,
      Test_offsets_,
      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _has_bits_[0]),
      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _unknown_fields_),
      -1,
      ::google::protobuf::DescriptorPool::generated_pool(),
      ::google::protobuf::MessageFactory::generated_factory(),
      sizeof(Test));
}
inline void protobuf_AssignDescriptorsOnce() {
  ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
                 &protobuf_AssignDesc_xxx_2eproto);
}

// message.h 中 message的基本接口.
virtual const Reflection* GetReflection() const {
    return GetMetadata().reflection;
}
// 每个message获取自己基本信息的接口.
::google::protobuf::Metadata Test::GetMetadata() const {
  protobuf_AssignDescriptorsOnce();
  ::google::protobuf::Metadata metadata;
  metadata.descriptor = Test_descriptor_;
  metadata.reflection = Test_reflection_;
  return metadata;
}

内存赋值,可以执行修改操作:


// 找到对应的内存地址,返回合适类型的指针.
template <typename Type>
inline Type* GeneratedMessageReflection::MutableRaw(
    Message* message, const FieldDescriptor* field) const {
  int index = field->containing_oneof() ?
      descriptor_->field_count() + field->containing_oneof()->index() :
      field->index();
  void* ptr = reinterpret_cast<uint8*>(message) + offsets_[index];
  return reinterpret_cast<Type*>(ptr);
}
// 设置protobuf的标志bit.
inline void GeneratedMessageReflection::SetBit(
    Message* message, const FieldDescriptor* field) const {
  if (has_bits_offset_ == -1) {
    return;
  }
  MutableHasBits(message)[field->index() / 32] |= (1 << (field->index() % 32));
}
// 设置某个字段的值
template <typename Type>
inline void GeneratedMessageReflection::SetField(
    Message* message, const FieldDescriptor* field, const Type& value) const {
  if (field->containing_oneof() && !HasOneofField(*message, field)) {
    ClearOneof(message, field->containing_oneof()); // V3 oneof 类型的清理。
  }
  *MutableRaw<Type>(message, field) = value; // 先直接覆盖
  field->containing_oneof() ?
      SetOneofCase(message, field) : SetBit(message, field); // 添加标记bit
}

2.5 、 反射原理具体实例介绍

参考
下面的代码展示了protobuf 对象反射的例子。将一个对象按照反射的字段顺序序列化到string,然后反序列化到对象。最后调用反射打印其字段值,可以看到对象能够还原。

1、proto文件

package tutorial;
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
 optional PhoneNumber phone = 4;

}
#include <string>
#include <map>
#include <iostream>
#include <stdio.h>
#include "person.pb.h"
using namespace tutorial;
using namespace google::protobuf;

using std::cout;
using std::endl;
using std::string;


void serialize_message(const google::protobuf::Message& message, std::string* serialized_string) {
    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) {
            //arrays not supported
            assert(!field->is_repeated());

            switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
                case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype:{\
                    valuetype value = reflection->Get##method(message, field);\
                    int wsize = field->name().size();\
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));\
                    serialized_string->append(field->name().c_str(), field->name().size());\
                    wsize = sizeof(value);\
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));\
                    serialized_string->append(reinterpret_cast<char*>(&value), sizeof(value));\
                    break;\
                }

                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);
#undef CASE_FIELD_TYPE
                case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
                    int value = reflection->GetEnum(message, field)->number();
                    int wsize = field->name().size();
                    //写入name占用字节数
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    //写入name
                    serialized_string->append(field->name().c_str(), field->name().size());
                    wsize = sizeof(value);
                    //写入value占用字节数
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    //写入value
                    serialized_string->append(reinterpret_cast<char*>(&value), sizeof(value));
                    break;
                }
                case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
                    std::string value = reflection->GetString(message, field);
                    int wsize = field->name().size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(field->name().c_str(), field->name().size());
                    wsize = value.size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(value.c_str(), value.size());
                    break;
                }
                case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
                    std::string value;
                    int wsize = field->name().size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(field->name().c_str(), field->name().size());
                    const google::protobuf::Message& submessage = reflection->GetMessage(message, field);
                    serialize_message(submessage, &value);
                    wsize = value.size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(value.c_str(), value.size());
                    break;
                }
            }
        }
    }
}
 

void parse_message(const std::string& serialized_string, google::protobuf::Message* message) {
    const google::protobuf::Descriptor* descriptor = message->GetDescriptor();
    const google::protobuf::Reflection* reflection = message->GetReflection();
    std::map<std::string, const google::protobuf::FieldDescriptor*> field_map;

    for (int i = 0; i < descriptor->field_count(); ++i) {
        const google::protobuf::FieldDescriptor* field = descriptor->field(i);
        field_map[field->name()] = field;
    }

    const google::protobuf::FieldDescriptor* field = NULL;
    size_t pos = 0;
    while (pos < serialized_string.size()) {
        int name_size = *(reinterpret_cast<const int*>(serialized_string.substr(pos, sizeof(int)).c_str()));
        pos += sizeof(int);
        std::string name = serialized_string.substr(pos, name_size);
        pos += name_size;

        int value_size = *(reinterpret_cast<const int*>(serialized_string.substr(pos, sizeof(int)).c_str()));
        pos += sizeof(int);
        std::string value = serialized_string.substr(pos, value_size);
        pos += value_size;

        std::map<std::string, const google::protobuf::FieldDescriptor*>::iterator iter =
            field_map.find(name);
        if (iter == field_map.end()) {
            fprintf(stderr, "no field found.\n");
            continue;
        } else {
            field = iter->second;
        }

        assert(!field->is_repeated());
        switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
            case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: {\
                reflection->Set##method(\
                        message,\
                        field,\
                        *(reinterpret_cast<const valuetype*>(value.c_str())));\
                std::cout << field->name() << "\t" << *(reinterpret_cast<const valuetype*>(value.c_str())) << std::endl;\
                break;\
            }
            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);
#undef CASE_FIELD_TYPE
            case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
                const google::protobuf::EnumValueDescriptor* enum_value_descriptor =
                    field->enum_type()->FindValueByNumber(*(reinterpret_cast<const int*>(value.c_str())));
                reflection->SetEnum(message, field, enum_value_descriptor);
                std::cout << field->name() << "\t" << *(reinterpret_cast<const int*>(value.c_str())) << std::endl;
                break;
            }
            case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
                reflection->SetString(message, field, value);
                std::cout << field->name() << "\t" << value << std::endl;
                break;
            }
            case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
                google::protobuf::Message* submessage = reflection->MutableMessage(message, field);
                parse_message(value, submessage);
                break;
            }
            default: {
                break;
            }
        }
    }
}
 

 

void print_field(const Message& message)
{
    const Descriptor* descriptor = message.GetDescriptor();
    const Reflection* reflection = message.GetReflection();

    for (int i = 0; i < descriptor->field_count(); ++i) {
        const FieldDescriptor* field = descriptor->field(i);
        bool has_field = reflection->HasField(message, field);
        assert(!field->is_repeated());

        switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
            case FieldDescriptor::CPPTYPE_##cpptype:{\
                valuetype value = reflection->Get##method(message, field);\
                if (has_field) {\
                    cout << field->name() << " : " << value << ", type : " << #valuetype << "\n";\
                }  else  {\
                    cout << field->name() << " : " << "None" << ", type : " << #valuetype << "\n";\
                }\
               break;\
            }

 
            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);
#undef CASE_FIELD_TYPE
            case FieldDescriptor::CPPTYPE_ENUM: {
                int value = reflection->GetEnum(message, field)->number();
                if (has_field)  {
                    cout << field->name() << " : " << value << ", type : " << "enum \n";    
                }
                else  {
                    cout << field->name() << " : " << "None" << ", type : " << "enum \n";
                }
                break;
            }
            case FieldDescriptor::CPPTYPE_STRING: {
               string value = reflection->GetString(message, field);
                if (has_field)  {
                    cout << field->name() << " : " << value << ", type : " << "string \n";  
                }
                else  {
                    cout << field->name() << " : " << "None" << ", type : " << "string \n";
                }
                break;
            }
            case FieldDescriptor::CPPTYPE_MESSAGE: {
                const Message& submessage = reflection->GetMessage(message, field);
                print_field(submessage);
                break;
            }
        }
    }
}

 

 

 

int main() 
{
    string str;
	Person person;
	person.set_name("shonm");
	person.set_id(123);
	person.mutable_phone()->set_number("1380000");
	person.mutable_phone()->set_type(Person_PhoneType_WORK);
	serialize_message(person, &str);       //按照自己的方式(反射的字段)序列化
	Person person2;
	parse_message(str, &person2);          //按照自己的方式反序列化
	printf("\n\n");
	print_field(person);                   //根据反射打印字段
	printf("\n\n");
	print_field(person2);
}

参考

1、https://www.jianshu.com/p/ddc1aaca3691
2、https://blog.csdn.net/cchd0001/article/details/52452204
3、https://blog.csdn.net/yindongjie1221/article/details/90575989
4、https://blog.csdn.net/boshuzhang/article/details/64920286
5、https://blog.csdn.net/zxm342698145/article/details/83931027

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值