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(); // 方式1constructor ctor = t2.get_constructor(); // 方式2var = 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_id和m_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_data中m_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++獲取類中成員函數的函數指針
?
總結
以上是生活随笔為你收集整理的RTTR(Run Time Type Reflection) C++反射原理实现剖解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小学计算机课玩的游戏,小学电脑课上偷偷玩
- 下一篇: 2018年度托福考情分析——口语篇