Contents
前言
Reflection:反射,这一特性在许多主流语言中都能找到,可以在运行时动态的获取类型信息。
一些人认为在cpp中编程时,可以规避掉使用反射,转而用模板来实现,或者使用反射就是程序设计出现了问题;要么就是有关于运行时反射对于性能影响,都用到反射了还不如去用Java之类的说辞。但我认为,在cpp中还是有一些需求是可以用到运行时反射的:对象的序列化与反序列化、可以通过字符串查找类型与创建对象、运行时判断继承关系等功能。
分析
反射信息属于一种元数据,这种元数据储存了类型相关的信息,如:类型名、基类、字段、方法、以及字段方法中的类型。这种在获取后也是储存在类中的,例如类的反射类型就是Type(C#)或Class(Java),字段的反射信息类就是FieldInfo等等。
以下文章储存反射信息的类将使用Type。
一般有三种方式获取反射信息Type实例:
第一种:使用字符串获取,Type::GetType(“xxclass”) 或 Class::forName(“xxclass”) 等等形式。
第二种:首先需要一个Object基类,该Object类的实例可以动态的获取一个元数据实例Type。也就是obj.GetType()
第三种:直接通过类型来获取,typeof(Object) 或 Object.class 等。
实现
基本实现思路:使用全局变量初始化变量的自定义结构体构造函数,通过模板元编程与手动传入等手段获取类型信息,最后向全局元数据信息表提交数据。
类型基础设计
在Object中编写一个私有静态的,用于获取类型信息的方法。
class Object { friend class Type; private: static Type* __meta_type(); };
声明Type类型:
class Type final : public Object { using c_inst_ptr_t = Object * (*)(const ParameterPackage&); private: int id_; string name_; int structure_size_; Type* base_; c_inst_ptr_t c_inst_ptr_; const std::type_info& typeinfo_; };
该Type类型记录了类型id,名字,sizeof的结果,基类对象指针,动态创建对象的工厂方法函数指针,标准库的type_info。
类型信息提交
那么如何生成这些Type实例呢?
使用在进main函数前,所有的全局变量都会初始化完毕这个特性,创建一个全局变量。这个全局变量将会自动收集相关信息后,提交或者注册到一个全局的Type表当中。我们可以轻松的利用C++17标准的静态内联变量来实现此功能。
class Object { friend class Type; private: static Type* __meta_type(); static inline struct _ObjectInit { _ObjectInit() { Object::__meta_type(); } } _object_init; };
静态的成员和全局变量本质是相同的,只不过静态成员的作用域与访问权限有所区别,所以依然可以参与全局的初始化。
该变量使用了自定义结构体类型,将会在初始化时自动构造。
Type* Object::__meta_type() { static int id = -1; if (id == -1) { id = Type::Register(CreateInstance, nullptr, _T("JxCoreLib::Object"), typeid(Object), sizeof(Object)); } return Type::GetType(id); }
生成一个静态变量id,默认为-1,代表没注册过该类型,如果不为-1,可以直接从Type中通过id快去获取类型。
向Type提交了
- 创建对象工厂方法的函数指针
- 基类Type,因为Object没有基类,所以nullptr
- 类型的名字,前面带上命名空间
- 标准库的类型信息
- 类型的大小
在Register之后返回一个内部Id,通过这个Id可以快速的获取Type实例,类似于缓存的作用。
实例对象获取Type
接下来在Object中添加一个虚函数:
class Object { friend class Type; private: static Type* __meta_type(); static inline struct _ObjectInit { _ObjectInit() { Object::__meta_type(); } } _object_init; virtual Type* get_type() const; };
实现:
Type* Object::get_type() const { return __meta_type(); }
使用样例:
Object obj; Type* type = obj.GetType();
类型获取Type
在Type中添加模板函数
template<typename T> static inline Type* Typeof() { return T::__meta_type(); }
虽然__meta_type是类型的私有成员,但是Type是Object的友元类,可以直接访问。
另外在全局声明一个,更加方便使用:
template<typename T> Type* cltypeof() { return Type::Typeof<T>(); }
因为typeof关键字在g++中作为编译器扩展存在,可能会发生冲突,所以前面加了cl。
使用样例:
assert( Object().GetType() == cltypeof<Object>() )
字符串获取Type
字符串获取就是对全局Type表的遍历,找到对应的名字后返回。
Type* Type::GetType(const string& str) { for (auto& item : *g_types) { if (item->get_name() == str) { return item; } } return nullptr; }