牵着老婆满街逛

严以律己,宽以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

google protobuf 反射机制学习笔记

转载自:http://www.voidcn.com/blog/cchd0001/article/p-6190303.html

何为反射机制

基本概念

指程序可以访问、检测和修改它本身状态或行为的一种能力

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

我(c++程序员)关注的问题

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

protobuf 反射使用简介

通过类型名字创建出类型对象.
//! 利用类型名字构造对象.
/*
! * @Param type_name 类型名字,比如 "Test.TestMessage". * @Return 对象指针,new 出来的,使用者负责释放. */
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>

inline google::protobuf::Message* allocMessage(const std::string& type_name)
{
    google::protobuf::Message* message = NULL;
    // 先获得类型的Descriptor .
    const google::protobuf::Descriptor* descriptor =
        google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(type_name);
    if (descriptor)
    {
        // 利用Descriptor拿到类型注册的instance. 这个是不可修改的.
        const google::protobuf::Message* prototype =
            google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
        if (prototype)
        {
            // 利用instance 构造出可以使用的对象.
            message = prototype->New();
        }
    }
    return message;
}
通过对象和对象的属性的名字获取,修改对应属性.

首先定义mesage :

package Test;
message Person {
    optional int32 id = 1 ;
}
#include "cpp/test.pb.h"
#include <iostream>

int main()
{
    Test::Person p_test ;
    // 拿到对象的描述包.
    auto descriptor = p_test.GetDescriptor() ;
    // 拿到对象的反射配置.
    auto reflecter = p_test.GetReflection() ;
    // 拿到属性的描述包.
    auto field = descriptor->FindFieldByName("id");
    // 设置属性的值.
    reflecter->SetInt32(&p_test , field , 5 ) ;
    // 获取属性的值.
    std::cout<<reflecter->GetInt32(p_test , field)<< std::endl ;
    return 0 ;
}

通过对象和对象方法的名字调用对应的方法.

//TODO

protobuf 反射实现解析.

基本概念

Descriptor系列.

::google::protobuf::Descriptor , 或者叫他google.protobuf.Descriptor更为恰当. 
Descriptor系列是一些用protobuf定义的,用来描述所有由protbuf产生的类型的类型信息包. 
对应的proto文件在 : https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto 
Descriptor 系列最大的message是 FileDescriptor . 每个文件会生成一个包含本文件所有信息的FileDescriptor包.

举个例子 : 
当你有一个test.proto 比如 :

package T;
message Test {
    optional int32 id = 1;
}

protoc 会给就会自动填装一个描述包,类似于:

::google::protobuf::FileDescriptor file;
file.set_name("test.proto");
file.set_packet("T")
auto desc = file.add_message_type() ;
desc->set_name("T.Test");
auto id_desc  = desc->mutable_field();
id_desc->set_name("id");
id_desc->set_type(::google::protobuf::FieldDescriptorProto::TYPE_INT32);
id_desc->set_number(1);
//

然后保存起来. 
如果你读protoc生成的 test.pb.cc文件 你会看到这样的代码 :

::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
 "\n\013test.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);

其实就是在代码中记录了对应proto文件的FileDescriptor包序列化之后的数据. 作为参数直接使用.

offset

任何一个对象最终都对应一段内存,有内存起始(start_addr)和结束地址, 
而对象的每一个属性,都位于 start_addr+$offset ,所以当对象和对应属性的offset已知的时候, 
属性的内存地址也就是可以获取的。

//! 获取某个属性在对应类型对象的内存偏移.
#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \
  static_cast<int>(                                           \
      reinterpret_cast<const char*>(                          \
          &reinterpret_cast<const TYPE*>(16)->FIELD) -        \
      reinterpret_cast<const char*>(16))

解决问题的办法

通过类型名字创建出类型对象.

查表!! 
是的,你没猜错,就是查表!!!

  • 数据存储在哪里

所有的Descriptor存储在单例的DescriptorPool 中。google::protobuf::DescriptorPool::generated_pool()来获取他的指针。 
所有的instance 存储在单例的MessageFactory中。google::protobuf::MessageFactory::generated_factory()来获取他的指针。

  • 将所有的Descriptor & instance 提前维护到表中备查

在protoc 生成的每个cc文件中, 都会有下面的代码(protobuf V2 版本) :

// xxx 应该替换为文件名,比如test.proto的test.

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_;
}

通过对象和对象的属性的名字获取,修改对应属性.

  • 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;
}

GeneratedMessageReflection 操作具体对象的属性

按照offset数组的提示,注解获取操作对应内存,这里以int32字段的SetInt32接口为例子.

    • 接口定义
      #undef DEFINE_PRIMITIVE_ACCESSORS
      #define DEFINE_PRIMITIVE_ACCESSORS(TYPENAME, TYPE, PASSTYPE, CPPTYPE)
      void GeneratedMessageReflection::Set##TYPENAME( \
            Message
      * message, const FieldDescriptor* field,                        \
            PASSTYPE value) 
      const {                                                \
          USAGE_CHECK_ALL(Set##TYPENAME, SINGULAR, CPPTYPE); \
          
      if (field->is_extension()) {    /*先不要在意这个*/                       \
            
      return MutableExtensionSet(message)->Set##TYPENAME( \
              field
      ->number(), field->type(), value, field);                       \
          } 
      else {
            
      /*一般的字段走这里*/\
            SetField
      <TYPE>(message, field, value);                                 \
          }                                                                        \
        }

      DEFINE_PRIMITIVE_ACCESSORS(Int32 , int32 , int32 , INT32 )
      #undef DEFINE_PRIMITIVE_ACCESSORS
      内存赋值.
      // 找到对应的内存地址,返回合适类型的指针.
      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
      }
      通过对象和对象方法的名字调用对应的方法.

posted on 2016-11-18 14:09 杨粼波 阅读(2069) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理