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提交了

  1. 创建对象工厂方法的函数指针
  2. 基类Type,因为Object没有基类,所以nullptr
  3. 类型的名字,前面带上命名空间
  4. 标准库的类型信息
  5. 类型的大小

在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;
}