RTTR(Run Time Type Reflection) C++反射原理实现剖解

C++反射是什么?

我个人理解的是运行时获取类的各种信息,类的成员变量,类的成员函数,更具体的说是运行时一个类知道自己有几个属性,知道每个属性的名字是什么, 可以通过名字运行时访问以及调用类的各种数据,这就是C++反射。当然非类的变量函数也是属于C++反射的范围,不过这里主要指代是Class(struct)相关的反射。看下面一段RTTR示例代码

#include <iostream>
#include<rttr/type>
#include <rttr/registration>
using namespace rttr;

class MyStruct 
{ 

public:
	MyStruct() 
	{

	};

	void func(double a) 
	{
		printf("a = %f\n", a);
	}; 

	int data; 
	RTTR_ENABLE()
};

class TestA : public MyStruct
{

public:
	TestA()
	{

	}

	RTTR_ENABLE(MyStruct)
};

class TestB : public TestA
{

public:
	TestB()
	{

	}

	RTTR_ENABLE(TestA)
};

//手动注册属性方法和构造函数
RTTR_REGISTRATION
{
	registration::class_<MyStruct>("MyStruct")
		 .constructor<>()
		 .property("adata", &MyStruct::data)
		 (
			 metadata("TOOL_TIP", "1121`Set the name of node."),
			 metadata("TOOL_TIP1", "1121`Set the name of node.")
		 )
		 .method("func", &MyStruct::func);

	registration::class_<TestA>("TestA")
		.constructor<>();

	registration::class_<TestB>("TestB")
		.constructor<>();
}

int main() {

	//遍历类的成员
	type t = type::get<MyStruct>();
	for (auto& prop : t.get_properties())
		std::cout << "name: " << prop.get_name() << std::endl;

	for (auto& meth : t.get_methods())
		std::cout << "name: " << meth.get_name() << std::endl;

	//创建类型的实例
	type t2 = type::get_by_name("MyStruct");
	variant var = t2.create();    // 方式1

	constructor ctor = t2.get_constructor();  // 方式2
	var = ctor.invoke();
	std::cout << "112= " << var.get_type().get_name() << std::endl;  // 打印类型名称

	//设置/获取属性
	MyStruct obj;

	property prop = type::get(obj).get_property("adata");
	prop.set_value(obj, 23);

	variant var_prop = prop.get_value(obj);
	
	std::cout << "metadata= " << prop.get_metadata("TOOL_TIP").to_string() << std::endl; // prints '23'
	std::cout << "1srfwe44= "<< prop.get_type().get_name() << std::endl; // prints '23'

	//调用方法
	MyStruct obj2;

	method meth = type::get(obj2).get_method("func");
	meth.invoke(obj2, 42.0);

	type a = type::get(obj2);
	variant var2 = a.create();
	meth.invoke(var2, 42.0);
	
	type b = type::get<TestB>();
	printf("b name is %s\n", b.get_name());
	array_range<type> baseClasses = b.get_base_classes();

	if (t.is_base_of(a))
	{
		printf("1111111111111111\n");
	}

	for (auto &baseclass : baseClasses)
	{
		printf("base class name is %s\n", baseclass.get_name());
	}

	auto ss = &MyStruct::data;
	
	std::cout << "fsdd11111= " << typeid(a).name() << std::endl; // prints '23'
	MyStruct aassdd;
	type cc = type::get(aassdd);
	std::cout << "aassdd= " << cc.get_name() << std::endl; // prints '23'
	system("pause");
	return 0;
}

输出:

 

RTTR的核心数据结构

type_register_private---------type

type-----property,method,metadata

 

下面的各种分析都是以上面代码例子来分析

C++反射-类型(type)的实现

RTTR数据类型类为 type,记录了“int,float,class等类型的类型数据”,比如 class MyStruct 的类型数据, 记录了类 MyStruct 的名字为“MyStruct ”

 

类型数据(type)的类型结构

类型数据(type)的注册流程

上面是大致的简略版调用函数栈, 简约归为:在运行时,获取一个类的type_data, 然后构造出type, 最后把type存储到type_private_data单例管理里。可以看到m_src_name_to_idm_custom_name_to_id是两个map, 以类型名为key,以type为value.

m_src_name_to_id存放的key是类型的原名,m_custom_name_to_id存放的key是后定制的名

这里有个非常重要的执行过程,构造type_data的过程,因为type_data存着type最重要的数据, 也就是类型的原生名

   

RTTR反射框架获取类型原名是统统编辑器内置宏来获取的,比如MSVC的__FUNCSIG__,GUNC的__PRETTY_FUNCTION__,CLANG或者APPLECLANG的__PRETTY_FUNCTION__,下面主要是实现代码

类型数据(type)的相关接口的实现

type::get(T&& object)

主要是模板推导, 从object推导其类型,就可以从type_private_data从遍历寻找相应的type数据

type::get_by_name(string_view name)

直接从type_private_data中遍历m_custom_name_to_id哈希表来查找type

 

C++反射-属性(property)的实现

类内的各种变量为属性, 类属性指针的表现比较特别。看看下面的例子,可以看出每个类内的变量类型是以类为前缀的,比如 MyStruct 的 int 变量成员类型就是int MyStruct::*, 我们可以通过类的实例对象和类变量指针进行访问。

类属性数据(property)的数据结构

类属性数据(property)的注册流程

上面是大致的简略版调用函数栈,简略归纳为 声明获取property的虚函数的接口基类property_wrapper_base。通过property_wapper模板类继承接口类,并利用模板推导一个属性为 “m_acc: A (C::*)”。而接口基类指针存在property里,property访问真正的类属性指针就忽视了类型,直接通过property_wrapper_base接口就能访问。这样既可以让class_data保存一个整齐无模板的property数组,又让property可以间接包含了类属性指针。

类型数据(property)的相关接口的实现

property get_property(string_view name)

实现上就是从type_data的class_data中property数组中遍历寻找

 

C++反射-方法(method)的实现

类内的各种执行函数为方法, 类方法指针和上面所说的类属性指针类似。看看下面的例子,可以看出每个类内的函数指针是以类为前缀的,比如 MyStruct PrintA 函数类型就是 void (MyStruct::*)(int), 我们可以通过类的实例对象和类变量指针进行访问。

类函数数据(method)的数据结构

RTTR框架中 method的数据结构和property的数据结构非常类似

类方法数据(method)的注册流程

上面是大致的简略版调用函数栈, 过程和property的非常类似。简略归纳为 声明获取method各种基础属性的虚函数的接口基类method_wrapper_base。通过method_wapper模板类继承接口类,并利用模板推导一个函数类型为 “F”。而接口基类指针存在method里,method访问真正的函数指针变量就忽视了类型,直接通过property_wrapper_base接口就能访问。这样既可以让class_data保存一个整齐无模板的method数组,又让method可以间接包含了类函数指针。

 

类型数据(method)的相关接口的实现

 method get_method(string_view name)

实现和get_property的类似,都是从class_datam_methods数组中遍历查找。

 

类型(type)的子类型(derived_class), 父类型(base_class)注册

个人举得 这部分是RTTR框架实现得最精彩的部分,先看案例代码。下面 RTTR_ENABLE 将 TestA父类注册为MyStrcut和A, 换句话说,同时也将MyStrcut 和 A的子类注册为TestA

derived_class和base_class的数据结构

 

derived_class和base_class的注册流程

base_class 和 derived_class的发生在创建一个type的type_data的时候

          

 

这里 DerivedClass为目前创建类型type数据的类, 也就是示例代码的“TestA”.    "has_base_class_list"假设一个类声明了base_class_list类型,就进行类型列表的递归编译(其中某些类型可能也声明了RTTR_ENABLE,存在类型列表),在静态函数中对每一个继承类型进行递归查找他们相应的type,然后加入vector<type>中,最终为type的type_data数据生成了base_classes(std::vector<type>)

这里最大的亮点在于:编译期进行递归,生成查找注册的类型列表中所有类型的代码。

当然此时type_data的class_data还没具备m_base_types和m_derived_types,子类型和父类型的实现是在注册type到 type_private_data单例管理类的时候,具体参考上面 “type生成的函数栈”。

value

 

metadata的实现

无论是类型的元数据,属性的元数据,方法的元数据,本质是就是key-value的map。实现方式很多,在RTTR里 property和mehod的元数据实现都是在相应的wrapper类继承metahandle, 没啥特殊的,所以就不详细分析实现原理。

 

参考资料

(1)__FUNCSIG__、__FUNCDNAME__、__FUNCTION__、__func__、__PRETTY_FUNCTION__

(2)C++ 类成员函数的函数指针

(3)C++获取类中成员函数的函数指针

 

  • 18
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以使用反射技术来获取类成员中的属性并以字符串方式输出。反射是一种在运行时检查、访问和修改程序元素的能力。在C++11中,可以使用一些库来实现反射,例如Boost.ReflectionRTTRRun Time Type Reflection)。 以下是一个使用Boost.Reflection库的示例代码,可以获取类成员中的属性并以字符串方式输出: ```c++ #include <iostream> #include <boost/reflection.hpp> class MyClass { public: MyClass(int id, const std::string& name) : id_(id), name_(name) {} int get_id() const { return id_; } const std::string& get_name() const { return name_; } private: int id_; std::string name_; }; int main() { MyClass obj(1, "Alice"); boost::reflection::Object obj_ref(obj); boost::reflection::Type obj_type = obj_ref.GetType(); std::vector<boost::reflection::Property> properties = obj_type.GetProperties(); for (const auto& property : properties) { std::cout << property.GetName() << ": " << property.GetValue(obj_ref) << std::endl; } return 0; } ``` 在上面的示例代码中,我们定义了一个名为MyClass的类,并在其中定义了成员变量id_和name_,以及成员函数get_id()和get_name()。接下来,在main函数中,我们创建了一个MyClass对象,并使用boost::reflection::Object类来获取对象的引用。然后,我们使用boost::reflection::Type类来获取对象的类型,并使用GetProperties()函数获取对象的所有属性。最后,我们使用属性的名称和GetValue()函数来输出属性的值。 需要注意的是,Boost.Reflection是一个第三方库,需要在项目中添加相应的头文件和链接库。如果不想使用第三方库,也可以手动实现反射功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值