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

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            google protobuf 反射機(jī)制學(xué)習(xí)筆記

            轉(zhuǎn)載自:http://www.voidcn.com/blog/cchd0001/article/p-6190303.html

            何為反射機(jī)制

            基本概念

            指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力

            程序集包含模塊,而模塊包含類型,類型又包含成員。反射則提供了封裝程序集、模塊和類型的對象。您可以使用反射動態(tài)地創(chuàng)建類型的實(shí)例,將類型綁定到現(xiàn)有對象,或從現(xiàn)有對象中獲取類型。然后,可以調(diào)用類型的方法或訪問其字段和屬性。

            我(c++程序員)關(guān)注的問題

            • 如何在程序運(yùn)行過程中通過類型名字(一個(gè)字符串,合法但是內(nèi)容在編譯期間未知,比如是在配置文件中獲取的)創(chuàng)建出類型對象.
            • 如果在程序運(yùn)行過程中通過對象和對象的屬性的名字(一個(gè)字符串,合法但是內(nèi)容在編譯期間未知,比如是通過通訊包獲取的)獲取,修改對應(yīng)屬性.
            • 如果在程序運(yùn)行過程中通過對象和對象方法的名字(一個(gè)字符串,合法但是內(nèi)容在編譯期間未知,比如是從用戶輸入獲取的)調(diào)用對應(yīng)的方法.

            protobuf 反射使用簡介

            通過類型名字創(chuàng)建出類型對象.
            //! 利用類型名字構(gòu)造對象.
            /*
            ! * @Param type_name 類型名字,比如 "Test.TestMessage". * @Return 對象指針,new 出來的,使用者負(fù)責(zé)釋放. */
            #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. 這個(gè)是不可修改的.
                    const google::protobuf::Message* prototype =
                        google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
                    if (prototype)
                    {
                        // 利用instance 構(gòu)造出可以使用的對象.
                        message = prototype->New();
                    }
                }
                return message;
            }
            通過對象和對象的屬性的名字獲取,修改對應(yīng)屬性.

            首先定義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");
                // 設(shè)置屬性的值.
                reflecter->SetInt32(&p_test , field , 5 ) ;
                // 獲取屬性的值.
                std::cout<<reflecter->GetInt32(p_test , field)<< std::endl ;
                return 0 ;
            }

            通過對象和對象方法的名字調(diào)用對應(yīng)的方法.

            //TODO

            protobuf 反射實(shí)現(xiàn)解析.

            基本概念

            Descriptor系列.

            ::google::protobuf::Descriptor , 或者叫他google.protobuf.Descriptor更為恰當(dāng). 
            Descriptor系列是一些用protobuf定義的,用來描述所有由protbuf產(chǎn)生的類型的類型信息包. 
            對應(yīng)的proto文件在 : https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto 
            Descriptor 系列最大的message是 FileDescriptor . 每個(gè)文件會生成一個(gè)包含本文件所有信息的FileDescriptor包.

            舉個(gè)例子 : 
            當(dāng)你有一個(gè)test.proto 比如 :

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

            protoc 會給就會自動填裝一個(gè)描述包,類似于:

            ::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);

            其實(shí)就是在代碼中記錄了對應(yīng)proto文件的FileDescriptor包序列化之后的數(shù)據(jù). 作為參數(shù)直接使用.

            offset

            任何一個(gè)對象最終都對應(yīng)一段內(nèi)存,有內(nèi)存起始(start_addr)和結(jié)束地址, 
            而對象的每一個(gè)屬性,都位于 start_addr+$offset ,所以當(dāng)對象和對應(yīng)屬性的offset已知的時(shí)候, 
            屬性的內(nèi)存地址也就是可以獲取的。

            //! 獲取某個(gè)屬性在對應(yīng)類型對象的內(nèi)存偏移.
            #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))

            解決問題的辦法

            通過類型名字創(chuàng)建出類型對象.

            查表!! 
            是的,你沒猜錯(cuò),就是查表!!!

            • 數(shù)據(jù)存儲在哪里

            所有的Descriptor存儲在單例的DescriptorPool 中。google::protobuf::DescriptorPool::generated_pool()來獲取他的指針。 
            所有的instance 存儲在單例的MessageFactory中。google::protobuf::MessageFactory::generated_factory()來獲取他的指針。

            • 將所有的Descriptor & instance 提前維護(hù)到表中備查

            在protoc 生成的每個(gè)cc文件中, 都會有下面的代碼(protobuf V2 版本) :

            // xxx 應(yīng)該替換為文件名,比如test.proto的test.

            namespace { 

            //! 將本文件內(nèi)的全部類型的instance注冊進(jìn)入MessageFactory的接口.
            void protobuf_RegisterTypes(const ::std::string&) {
               // 初始化本文件的reflection數(shù)據(jù).
              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獲取對應(yīng)的Descriptor。
              ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
                "\n\013xxx.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);
              // 將本文件的類型instance注冊接口注冊給MessageFactory.
              
            // 這里注冊接口是為了實(shí)現(xiàn)類型的lazy注冊。如果沒有使用請求某個(gè)文件的類型,就不注冊對應(yīng)文件的類型。
              ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
                "xxx.proto", &protobuf_RegisterTypes);
              // 構(gòu)造并且初始化全部instance.
              Test::default_instance_ = new Test();
              Test::default_instance_->InitAsDefaultInstance();
              // 注冊清理接口.
              ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_xxx_2eproto);
            }
            //! 下面利用全局變量的構(gòu)造函數(shù)確保main函數(shù)執(zhí)行之前數(shù)據(jù)已經(jīng)進(jìn)行注冊.
            struct StaticDescriptorInitializer_xxx_2eproto {
              StaticDescriptorInitializer_xxx_2eproto() {
                protobuf_AddDesc_xxx_2eproto();
              }
            } static_descriptor_initializer_xxx_2eproto_;
            }

            通過對象和對象的屬性的名字獲取,修改對應(yīng)屬性.

            • GeneratedMessageReflection 的填裝和獲取

            對于每一個(gè)message , 都有一個(gè)對應(yīng)的GeneratedMessageReflection 對象. 
            這個(gè)對象保存了對應(yīng)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] = {
                //這里在計(jì)算屬性的內(nèi)存偏移.
                GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, id_),
              };
              // 這里是個(gè)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;
            }
            // 每個(gè)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數(shù)組的提示,注解獲取操作對應(yīng)內(nèi)存,這里以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()) {    /*先不要在意這個(gè)*/                       \
                      
                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
                內(nèi)存賦值.
                // 找到對應(yīng)的內(nèi)存地址,返回合適類型的指針.
                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);
                }
                // 設(shè)置protobuf的標(biāo)志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));
                }
                // 設(shè)置某個(gè)字段的值
                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); 
                // 添加標(biāo)記bit
                }
                通過對象和對象方法的名字調(diào)用對應(yīng)的方法.

            posted on 2016-11-18 14:09 楊粼波 閱讀(2817) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久九九亚洲精品| 久久综合丝袜日本网| 亚洲精品高清国产一线久久| 无码专区久久综合久中文字幕 | 一级做a爰片久久毛片16| 999久久久国产精品| 亚洲精品tv久久久久| 久久这里只有精品18| 99久久精品免费看国产| 久久婷婷午色综合夜啪| 国产精品国色综合久久| 久久精品国产色蜜蜜麻豆| 中文字幕久久精品 | 久久国产午夜精品一区二区三区| 亚洲国产日韩欧美综合久久| 精品熟女少妇av免费久久| 久久精品亚洲男人的天堂| 麻豆一区二区99久久久久| 精品国产综合区久久久久久 | 久久精品国产影库免费看| 色婷婷久久久SWAG精品| 99久久人妻无码精品系列蜜桃| 人妻丰满?V无码久久不卡| 国产精品久久永久免费| 久久天天婷婷五月俺也去| 国产精品久久自在自线观看| 久久久久久精品成人免费图片| 久久这里只有精品久久| 日韩人妻无码一区二区三区久久 | 久久精品免费一区二区| 国产精品成人99久久久久| 久久中文骚妇内射| 久久这里只精品99re66| 精品国产婷婷久久久| 精品免费tv久久久久久久| 99久久精品免费看国产一区二区三区 | 国产精品内射久久久久欢欢| 亚洲国产欧洲综合997久久| 久久影视综合亚洲| 久久综合狠狠综合久久激情 | 久久人人爽人人爽人人片av高请 |