持续更新中。。。
何为反射机制
基本概念
指程序可以访问、检测和修改它本身状态或行为的一种能力
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
我(c++程序员)关注的问题
- 如何在程序运行过程中通过类型名字(一个字符串,合法但是内容在编译期间未知,比如是在配置文件中获取的)创建出类型对象.
- 如果在程序运行过程中通过对象和对象的属性的名字(一个字符串,合法但是内容在编译期间未知,比如是通过通讯包获取的)获取,修改对应属性.
- 如果在程序运行过程中通过对象和对象方法的名字(一个字符串,合法但是内容在编译期间未知,比如是从用户输入获取的)调用对应的方法.
protobuf 反射使用简介
举个例子 :
当你有一个test.proto 比如 :
package T;
message Test
{
optional int32 id = 1;
}
通过类型名字创建出类型对象.
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include "cpp/test.pb.h"
int main()
{
auto descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("T.Test");
if (nullptr == descriptor)
{
return 0 ;
}
auto prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
if ( nullptr == descriptor)
{
return 0 ;
}
auto message = prototype->New();
auto test = dynamic_cast<T::Test*>(message);
test->set_id(1);
std::cout<<test->Utf8DebugString()<<std::endl;
delete message ;
return 0 ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
#include <iostream>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
int main()
{
google::protobuf::compiler::DiskSourceTree sourceTree;
sourceTree.MapPath("project_root","./");
google::protobuf::compiler::Importer importer(&sourceTree, NULL);
importer.Import("project_root/source_proto/test.proto");
auto descriptor1 = importer.pool()->FindMessageTypeByName("T.Test");
google::protobuf::DynamicMessageFactory factory;
auto proto1 = factory.GetPrototype(descriptor1);
auto message1= proto1->New();
auto reflection1 = message1->GetReflection();
auto filed1 = descriptor1->FindFieldByName("id");
reflection1->SetInt32(message1,filed1,1);
std::cout << message1->DebugString();
delete message1 ;
return 0 ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
通过对象和对象的属性的名字获取,修改对应属性.
首先定义mesage :
对于上文提到的 test.proto
#include "cpp/test.pb.h"
#include <iostream>
int main()
{
T::Test 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 ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
通过对象和对象方法的名字调用对应的方法.
//TODO
protobuf 反射实现解析.
基本概念
Descriptor系列.
::google::protobuf::Descriptor
系列包括:
- Descriptor – 用来描述 消息
- FieldDescriptor – 用来描述 字段
- OneofDescriptor – 用来描述 联合体
- EnumDescriptor – 用来描述 枚举
- EnumValueDescriptor – 用来描述 枚举值
- ServiceDescriptor – 用来描述 服务器
- MethodDescriptor – 用来描述 服务器方法
- FileDescriptor – 用来描述 文件
这些 Descriptor
系列的数据则是由 DescriptorProto
系列的数据 , 利用DescriptorBuilder工具类来填充的 .
有兴趣的可以查阅 https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.cc
DescriptorProto
系列是一些用protobuf定义的,用来描述所有由protbuf产生的类型的类型信息包.
对应的proto文件在 : https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto
Descriptor
系列最大的message是 FileDescriptor
. 每个文件会生成一个包含本文件所有信息的FileDescriptor
包.
举个例子 :
对于上文提到的 test.proto , protoc 会给就会自动填装一个描述包,类似于:
::google::protobuf::FileDescriptorProto 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);
其实就是在protoc生成代码中hard code 了对应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))
DescriptorDatabase
DescriptorDatabase
是一个纯虚基类,描述了一系列符合通过名字(文件名,符号名。。。) 来获取FileDescriptorProto
的接口 :
https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor_database.h
class LIBPROTOBUF_EXPORT DescriptorDatabase {
public:
inline DescriptorDatabase() {}
virtual ~DescriptorDatabase();
virtual ~DescriptorDatabase();
virtual bool FindFileByName(const string& filename,
FileDescriptorProto* output) = 0;
virtual bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output) = 0;
virtual bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output) = 0;
virtual bool FindAllExtensionNumbers(const string& ,
vector<int>* ) {
return false;
}
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase);
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
核心的两个派生类是 :
这里我不探究protobuf 是如何运行时编译proto源文件.
DescriptorPool
任何时候想要查询一个Descriptor
, 都是去DescriptorPool
里面查询。
DescriptorPool
实现了这样的机制 :
- 缓存所有查询的文件的
Descriptor
。 - 查找
Descriptor
的时候,如果自身缓存查到就直接返回结果,
否则去自带的DescriptorDatabase
中查FileDescriptorProto
,
查到就转化成Descriptor
, 返回结果并且缓存.
https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.h
class LIBPROTOBUF_EXPORT DescriptorPool{
public:
DescriptorPool();
class ErrorCollector;
explicit DescriptorPool(DescriptorDatabase* fallback_database,
ErrorCollector* error_collector = NULL);
~DescriptorPool();
static const DescriptorPool* generated_pool();
const FileDescriptor* FindFileByName(const string& name) const;
class LIBPROTOBUF_EXPORT ErrorCollector {
};
const FileDescriptor* BuildFile(const FileDescriptorProto& proto);
const FileDescriptor* BuildFileCollectingErrors(
const FileDescriptorProto& proto,
ErrorCollector* error_collector);
void AllowUnknownDependencies() { allow_unknown_ = true; }
void EnforceWeakDependencies(bool enforce) { enforce_weak_ = enforce; }
private:
bool TryFindFileInFallbackDatabase(const string& name) const;
bool TryFindSymbolInFallbackDatabase(const string& name) const;
bool TryFindExtensionInFallbackDatabase(const Descriptor* containing_type,
int field_number) const;
DescriptorDatabase* fallback_database_;
scoped_ptr<Tables> tables_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPool);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
核心的查找接口
https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.cc
Symbol DescriptorPool::Tables::FindByNameHelper(
const DescriptorPool* pool, const string& name) {
MutexLockMaybe lock(pool->mutex_);
known_bad_symbols_.clear();
known_bad_files_.clear();
Symbol result = FindSymbol(name);
if (result.IsNull() && pool->underlay_ != NULL) {
result =
pool->underlay_->tables_->FindByNameHelper(pool->underlay_, name);
}
if (result.IsNull()) {
if (pool->TryFindSymbolInFallbackDatabase(name)) {
result = FindSymbol(name);
}
}
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
MessageFactory
任何时候想要获取一个类型的instance , 都要去MessageFactory
里面获取。
MessageFactory
是一个纯虚的基类,定义了通过Descripor来获取对应类型instance的接口.
{
public:
inline MessageFactory() {}
virtual ~MessageFactory();
virtual const Message* GetPrototype(const Descriptor* type) = 0;
static MessageFactory* generated_factory();
static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
const Message* prototype);
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory);
}
同样有两个核心的派生类
GeneratedMessageFactory
- 一个map , 保存着
Descriptor
和Message *
- 这个类对应着预先编译链接好的那些类型的反射机制。
DynamicMessageFactory
- 有简单的缓存,保存自己解析过的
Descriptor`` </li>
<li>可以通过
Descriptor“,动态的基于内存构造出一个Message
!!!
解决问题的办法
通过类型名字创建出类型对象 — 预编译proto并且链接进入二进制.
查表!!
是的,你没猜错,就是查表!!!
-
数据存储在哪里
所有的Descriptor
存储在单例的DescriptorPool
中。google::protobuf::DescriptorPool::generated_pool()
来获取他的指针。
所有的instance
存储在单例的MessageFactory
中。google::protobuf::MessageFactory::generated_factory()
来获取他的指针。
-
将所有的Descriptor & instance 提前维护到表中备查
在protoc 生成的每个cc文件中, 都会有下面的代码(protobuf V2 版本) :
namespace {
void protobuf_RegisterTypes(const ::std::string&) {
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;
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
"\n\013xxx.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"xxx.proto", &protobuf_RegisterTypes);
Test::default_instance_ = new Test();
Test::default_instance_->InitAsDefaultInstance();
::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_xxx_2eproto);
}
struct StaticDescriptorInitializer_xxx_2eproto {
StaticDescriptorInitializer_xxx_2eproto() {
protobuf_AddDesc_xxx_2eproto();
}
} static_descriptor_initializer_xxx_2eproto_;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
通过类型名字创建出类型对象 — 运行时态编译proto
这里要引入 Importer
类.
class LIBPROTOBUF_EXPORT Importer {
public:
Importer(SourceTree* source_tree,
MultiFileErrorCollector* error_collector);
~Importer();
const FileDescriptor* Import(const string& filename);
inline const DescriptorPool* pool() const {
return &pool_;
}
void AddUnusedImportTrackFile(const string& file_name);
void ClearUnusedImportTrackFiles();
private:
SourceTreeDescriptorDatabase database_;
DescriptorPool pool_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
通过对象和对象的属性的名字获取,修改对应属性.
- GeneratedMessageReflection 的填装和获取
对于每一个message , 都有一个对应的GeneratedMessageReflection 对象.
这个对象保存了对应message反射操作需要的信息.
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_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);
}
virtual const Reflection* GetReflection() const {
return GetMetadata().reflection;
}
::google::protobuf::Metadata Test::GetMetadata() const {
protobuf_AssignDescriptorsOnce();
::google::protobuf::Metadata metadata;
metadata.descriptor = Test_descriptor_;
metadata.reflection = Test_reflection_;
return metadata;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
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);
}
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());
}
*MutableRaw<Type>(message, field) = value;
field->containing_oneof() ?
SetOneofCase(message, field) : SetBit(message, field);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
通过对象和对象方法的名字调用对应的方法.
//TODO