青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴以律己,寬以待人. 三思而后行.
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 楊粼波 閱讀(2934) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲欧美日韩专区| 欧美一级一区| 久久国产88| 亚洲精品网址在线观看| 亚洲伊人网站| 日韩视频第一页| 久久永久免费| 久久精品国产亚洲精品| 国产精品av免费在线观看| 欧美成人综合网站| 国产亚洲欧美中文| 亚洲午夜一区| 亚洲一区二区av电影| 欧美刺激性大交免费视频 | 亚洲在线中文字幕| 鲁大师影院一区二区三区| 亚洲欧美视频在线观看视频| 免费成人av在线看| 免费久久99精品国产自| 国产亚洲精品bt天堂精选| 亚洲丝袜av一区| 亚洲制服av| 国产精品v日韩精品v欧美精品网站| 久久天堂成人| 红桃视频国产一区| 久久久久久有精品国产| 久久久亚洲国产天美传媒修理工| 国产精品一区免费视频| 亚洲一区自拍| 欧美自拍偷拍| 国产一区二区三区奇米久涩| 西瓜成人精品人成网站| 久久激情网站| 狠色狠色综合久久| 毛片一区二区| 亚洲国产精品一区制服丝袜 | 午夜日韩电影| 久久国内精品视频| 国产亚洲人成a一在线v站| 欧美一区二区播放| 久久久久综合网| 亚洲国产精品ⅴa在线观看| 美国十次成人| 亚洲精品国产视频| 亚洲综合三区| 国内精品久久久久影院色| 久久久久在线观看| 亚洲欧洲一区二区三区在线观看| 99视频精品全部免费在线| 欧美肉体xxxx裸体137大胆| 亚洲一区二区黄| 久久夜色精品国产欧美乱| 樱花yy私人影院亚洲| 欧美福利在线观看| 亚洲在线日韩| 欧美刺激性大交免费视频| 一区二区三区产品免费精品久久75 | 久久一区激情| 日韩视频在线观看免费| 欧美一区二区三区免费在线看| 国内精品久久久久久久97牛牛| 欧美激情久久久| 午夜天堂精品久久久久 | 一区二区三区精密机械公司 | 午夜影院日韩| 亚洲国产精品一区在线观看不卡 | 欧美国产欧美亚洲国产日韩mv天天看完整 | 国产欧美一区二区在线观看| 欧美一区二区三区四区夜夜大片 | av成人激情| 久久成年人视频| 亚洲国产精品成人久久综合一区| 欧美伦理91i| 一本久久a久久免费精品不卡| 欧美在线免费看| 亚洲第一视频网站| 欧美日韩亚洲一区| 亚洲免费影视第一页| 久久精品视频播放| 中日韩视频在线观看| 国产亚洲精品福利| 欧美激情视频一区二区三区在线播放| 亚洲精品乱码久久久久| 久久久国产精彩视频美女艺术照福利| 亚洲免费观看在线观看| 国产手机视频精品| 欧美大片在线看免费观看| 一区二区三区www| 麻豆精品国产91久久久久久| 午夜精品久久久久久久99热浪潮| 在线观看亚洲视频| 国产精品久久久久久久久婷婷| 久久久久久尹人网香蕉| 亚洲夜晚福利在线观看| 亚洲巨乳在线| 欧美激情小视频| 久久久精品日韩欧美| 国产精品99久久久久久久女警| 亚洲国产欧美日韩精品| 国户精品久久久久久久久久久不卡| 欧美黄色大片网站| 久久久久久亚洲精品中文字幕| 亚洲一区二区三区在线播放| 亚洲国产一区二区三区青草影视 | 欧美一区二区免费| 亚洲美女福利视频网站| 亚洲精品国产精品乱码不99按摩| 国产欧美一区在线| 国产精品九九久久久久久久| 欧美午夜精品久久久| 欧美日韩黄色大片| 久久综合伊人77777| 蜜乳av另类精品一区二区| 午夜精品999| 亚洲一区国产视频| 在线亚洲美日韩| 亚洲一区中文字幕在线观看| 99亚洲一区二区| 亚洲三级免费电影| 女仆av观看一区| 免费成人在线视频网站| 欧美在线啊v| 欧美一区二区三区啪啪| 欧美一区二区黄| 欧美在线观看www| 亚洲欧美日韩天堂| 久久精品国产视频| 久久综合伊人77777麻豆| 久久精品夜色噜噜亚洲aⅴ| 欧美一区二区三区播放老司机| 久久精品国产一区二区三| 久久精品人人做人人爽电影蜜月| 欧美在线观看视频一区二区| 欧美一级在线播放| 欧美在线观看一区二区| 蜜臀久久99精品久久久画质超高清| 麻豆成人在线播放| 欧美激情精品久久久久久蜜臀| 亚洲风情亚aⅴ在线发布| 野花国产精品入口| 亚洲一区二区在线免费观看| 亚洲专区在线视频| 欧美一区高清| 欧美精品久久99久久在免费线| 欧美日韩综合在线免费观看| 国产精品久久毛片a| 国产一区二区三区高清播放| 在线成人国产| 亚洲一区二区成人| 久久精品亚洲一区二区| 欧美风情在线观看| 欧美成人精品三级在线观看| 亚洲精选国产| 欧美在线视频观看免费网站| 老司机精品导航| 国产精品网站在线播放| 在线观看亚洲视频| 一区二区欧美在线| 久久精品99国产精品| 欧美国产一区二区在线观看| 日韩一级黄色大片| 久久精品国产亚洲精品| 欧美顶级少妇做爰| 韩国欧美一区| 亚洲图片欧美一区| 久久久久成人精品免费播放动漫| 欧美激情精品久久久久久大尺度| 亚洲视频在线观看| 亚洲欧美日韩国产一区| 欧美精品日韩综合在线| 国产一区二区三区高清播放| 老色鬼精品视频在线观看播放| 欧美一区二区精品| 亚洲香蕉在线观看| 久热re这里精品视频在线6| 亚洲先锋成人| 欧美成人精品在线观看| 国产亚洲欧洲| 亚洲自拍偷拍网址| 亚洲国产影院| 久久精品国产亚洲精品| 国产精品制服诱惑| 亚洲精品视频一区| 久热精品在线| 亚洲天堂网在线观看| 免费人成精品欧美精品| 国产乱人伦精品一区二区| 夜夜夜久久久| 日韩视频在线观看免费| 欧美成人精品一区二区三区| 韩国视频理论视频久久| 亚洲一区3d动漫同人无遮挡| 99国产精品久久久久久久成人热| 卡通动漫国产精品| 国产日韩一区| 一本久道久久久| 一本一本久久| 欧美日韩一区二区三区视频| 日韩视频永久免费观看| 日韩视频欧美视频|