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

            牽著老婆滿街逛

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

            国产亚洲色婷婷久久99精品| 狠狠色丁香婷婷久久综合| 久久国内免费视频| 韩国三级中文字幕hd久久精品 | 一本大道久久香蕉成人网| 99久久精品国产一区二区蜜芽| 国内精品久久久久影院一蜜桃 | 日本精品久久久中文字幕| 99久久成人国产精品免费 | 伊人久久大香线蕉精品| 欧美精品一本久久男人的天堂| 久久精品国产亚洲av水果派| 人妻精品久久久久中文字幕一冢本| 日韩av无码久久精品免费| 久久久国产精品亚洲一区| 久久免费小视频| 国产精品综合久久第一页| 性高湖久久久久久久久AAAAA | 久久国产成人亚洲精品影院| 91久久香蕉国产熟女线看| 久久97久久97精品免视看秋霞 | 九九久久精品无码专区| 欧美与黑人午夜性猛交久久久| 香蕉久久久久久狠狠色| 无码精品久久久久久人妻中字| 国产精品久久永久免费| 久久亚洲国产成人精品无码区| 中文字幕精品久久久久人妻| 久久久久久夜精品精品免费啦| 91精品国产高清久久久久久91| 久久伊人色| 久久精品国产91久久麻豆自制 | 亚洲成色www久久网站夜月 | 久久久久亚洲AV无码麻豆| 亚洲综合日韩久久成人AV| 亚洲AV无码久久寂寞少妇| 久久福利青草精品资源站| 亚洲αv久久久噜噜噜噜噜| 久久乐国产综合亚洲精品| 国产麻豆精品久久一二三| 久久人人爽人人爽人人片AV不 |