何為反射機(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ò),就是查表!!!
所有的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)的方法.