介绍
RTTR代表运行时类型反射。它描述了计算机程序在运行时查询和修改对象的能力。这也是库本身的名称,它是用C++编写的,并作为开源库发布。您可以在以下网址找到更多信息:www.rttr.org
安装
从github上下载rttr代码包,将其解压缩后运行CMake。将src下的rttr文件夹和编译完成的库一起拷走。
运行样例以确定配置成功:
#include<rttr/registration>
#include<iostream>
using namespace rttr;
using namespace std;
class MyStruct
{
public:
MyStruct(){};
void func(double){};
int data;
RTTR_ENABLE()
};
RTTR_REGISTRATION
{
registration::class_<MyStruct>("MyStruct")
.constructor<>()
.property("data",&MyStruct::data)
.method("func",&MyStruct::func);
}
int main(int argc, char const *argv[])
{
type t=type::get<MyStruct>();
for(auto &prop:t.get_properties())
cout<<"name: "<<prop.get_name()<<endl;
for(auto &meth:t.get_methods())
cout<<"name: "<<meth.get_name()<<endl;
return 0;
}
输出应为:
name: data
name: func
正式开始!
注册
在RTTR中,所有反射的标识都应手动注册。正式的注册应以
RTTR_REGISTRATION
{
为开始。
例如,以下代码注册了一个名为func的函数,一个名为aaa的变量和一个名为A的枚举:
RTTR_REGISTRATION
{
registration::method("func",func);
registration::property("aaa",aaa);
registration::enumeration<A>("A")
(
value("a",A::a),
value("b",A::b),
value("c",A::c)
);
}
对于多个函数的重载,应使用static_cast
指定。
如,指定参数为int,long long,string的重载函数:
static_cast<void(Active::*)(int,long long,string)>(&Active::try_one)
注册类也很简单。使用registration::class_
可以注册一个类。
如:
RTTR_REGISTRATION
{
registration::class_<MyStruct>("MyStruct")
.constructor<>()
.property("data",&MyStruct::data)
.method("func",&MyStruct::func);
}
注册的类必须包含RTTR_ENABLE()
。或者如果需要访问私有属性,请导入rttr/registration_friend
并将RTTR_REGISTRATION_FRIEND
加入类中。
我们还可以使用registration::property_readonly
注册一个只读属性!只读属性无法通过反射进行修改。
添加 access_levels
可以指定此属性的访问等级,有三个等级可选:
access_levels::public_access
access_levels::private_access
access_levels::protected_access
type类
我们可以使用type类访问已经注册过的信息。
例如,获取一个支持rttr反射的类的type:
Active a;
...
type ty=a.get_type();
type对象会描述一个特定对象的属性。可能最常用的type方法就是get_name
。这个方法将返回对象的名字。例如,下面这条语句:
cout<<a.get_type().get_name();
则会输出:
Active
还可以使用静态方法get_by_name
获取相应的对象:
const char *name="Active";
type ty=type::get_by_name(name);
可以使用is_valid
方法判断此对象是否存在。
也可以使用另一种方式获得type对象:
type ty1=type::get<Active>();
type ty2=type::get<int>();
type ty3=type::get<double*>();
可以使用==运算符比较两个type对象。例如:
if(a.get_type()==type::get<Active>()) ...
可以使用type类构造类的实例。调用get_constructor
方法将得到一个constructor
类型的对象,然后使用invoke
方法来构造一个实例。例如:
const char *name="Active";
type ty=type::get_by_name(name);
variant v=ty.get_constructor().invoke();
调用v.get_value<Active>
可以得到这个值。
如果这个类没有无参数的构造器,则get_constructor
方法不会有任何作用。
使用rttr分析类
rttr命名空间下有三个类property,method 和 constructor,分别用于描述变量,方法和构造器。这三个类都有一个名为 get_name
的方法,用来返回变量,方法或构造器的名字。property 类有一个 get_type
方法,用来返回描述变量类型的一个对象,这个对象的类型同样是 type。method 和 constructor 类有报告参数类型的方法,method类还有一个报告返回类型的方法。
type类中的get_properties
,get_methods
和get_constructors
方法将分别返回这个类中声明的字段,方法和构造器的列表(不可变),但不包括父类的成员。
下面的程序显示了如何打印一个类的全部信息。你可能需要花费较长的时间来注册这个类。
#include<rttr/registration>
#include<iostream>
using namespace rttr;
using namespace std;
RTTR_REGISTRATION
{
registration::class_<method>("method")
.method("is_valid",&method::is_valid)
.method("operator bool",&method::operator bool)
.method("get_name",&method::get_name)
.method("get_access_level",&method::get_access_level)
.method("is_static",&method::is_static)
.method("get_return_type",&method::get_return_type)
.method("get_declaring_type",&method::get_declaring_type)
.method("get_parameter_infos",&method::get_parameter_infos)
.method("get_signature",&method::get_signature)
.method("get_metadata",&method::get_metadata)
.method("invoke",static_cast<variant(method::*)(instance) const>(&method::invoke))
.method("invoke",static_cast<variant(method::*)(instance,argument) const>(&method::invoke))
.method("invoke",static_cast<variant(method::*)(instance,argument,argument) const>(&method::invoke))
.method("invoke",static_cast<variant(method::*)(instance,argument,argument,argument) const>(&method::invoke))
.method("invoke",static_cast<variant(method::*)(instance,argument,argument,argument,argument) const>(&method::invoke))
.method("invoke",static_cast<variant(method::*)(instance,argument,argument,argument,argument,argument) const>(&method::invoke))
.method("invoke",static_cast<variant(method::*)(instance,argument,argument,argument,argument,argument,argument) const>(&method::invoke))
.method("invoke_variadic",&method::invoke_variadic)
.method("operator==",&method::operator==)
.method("operator!=",&method::operator!=);
}
void print_base(type ty)
{
auto l=ty.get_base_classes();
int j=0;
for(auto i=l.begin();i!=l.end();++i,j++) // base classes应在RTTR_ENABLE宏中指定
{
cout<<": ";
cout<<i->get_name();
}
cout<<endl;
}
void print_methods(type ty)
{
for(auto i:ty.get_methods())
{
cout<<" ";
if(i.is_static())
cout<<"static ";
cout<<i.get_return_type().get_name();
cout<<' '<<i.get_name();
cout<<'(';
if(i.get_parameter_infos().size()!=0)
{
int k=0;
for(auto j:i.get_parameter_infos())
{
if(k!=0)
cout<<',';
cout<<j.get_type().get_name();
k+=1;
}
}
cout<<");"<<endl;
}
}
void print_prop(type ty)
{
for(auto i:ty.get_properties())
{
cout<<" ";
if(i.is_static())
cout<<"static ";
if(i.is_readonly())
cout<<"const ";
cout<<i.get_type().get_name();
cout<<' '<<i.get_name();
cout<<endl;
}
}
void print_constructors(type ty)
{
for(auto i:ty.get_constructors())
{
cout<<" ";
cout<<i.get_signature();
cout<<' ';
cout<<i.get_declaring_type().get_name();
cout<<" (";
int k=0;
for(auto j:i.get_parameter_infos())
{
if(k!=0)
cout<<", ";
cout<<j.get_type().get_name();
}
cout<<");"<<endl;
}
}
int main(int argc, char const *argv[])
{
type ty=type::get<method>();
cout<<"class "<<ty.get_name();
print_base(ty);
cout<<"{"<<endl;
cout<<"public:"<<endl;
print_methods(ty);
print_prop(ty);
print_constructors(ty);
cout<<"}";
return 0;
}
它将会输出:
class method
{
public:
bool is_valid();
bool operator bool();
rttr::basic_string_view<char,std::char_traits<char>> get_name();
rttr::access_levels get_access_level();
bool is_static();
rttr::type get_return_type();
rttr::type get_declaring_type();
rttr::array_range<rttr::parameter_info,rttr::detail::default_predicate<rttr::parameter_info>> get_parameter_infos();
rttr::basic_string_view<char,std::char_traits<char>> get_signature();
rttr::variant get_metadata(rttr::variant);
rttr::variant invoke(rttr::instance);
rttr::variant invoke(rttr::instance,rttr::argument);
rttr::variant invoke(rttr::instance,rttr::argument,rttr::argument);
rttr::variant invoke(rttr::instance,rttr::argument,rttr::argument,rttr::argument);
rttr::variant invoke(rttr::instance,rttr::argument,rttr::argument,rttr::argument,rttr::argument); rttr::variant invoke(rttr::instance,rttr::argument,rttr::argument,rttr::argument,rttr::argument,rttr::argument);
rttr::variant invoke(rttr::instance,rttr::argument,rttr::argument,rttr::argument,rttr::argument,rttr::argument,rttr::argument);
rttr::variant invoke_variadic(rttr::instance,std::vector<rttr::argument,std::allocator<rttr::argument>>);
bool operator==(method);
bool operator!=(method);
}
使用constructor创建类的实例
在rttr中,可以方便的使用constructor类生成一个类的实例。
我们需要先获得一个构造器:
auto con=ty.get_constructor({type::get<int>()});
表示获取一个参数为int的构造器。
接着我们需要构造它:
variant v=con.invoke(10);
调用invoke时,最多只能有六个参数。
variant类 类似any,表示可以存储任意类型的变量。
最后,我们需要获得构造完成的实例:
Active a=v.get_value<Active>();
使用method类运行函数
在rttr中提供了一个method类,可以方便的让我们运行一个函数。
运行函数的方法如下:
func.invoke(var,...)
如果func是一个静态函数,那么应为 {}
否则var为这个类的实例。
后面的参数表示这个函数的参数,超过六个的参数应该用 {} 包裹。
下面的代码演示了如何使用method类调用sqrt和log:
#include<rttr/registration>
#include<iostream>
#include<cmath>
using namespace rttr;
using namespace std;
RTTR_REGISTRATION
{
registration::method("sqrt",static_cast<long double(*)(long double)>(sqrt));
registration::method("log2",static_cast<long double(*)(long double)>(log2));
registration::method("log",static_cast<long double(*)(long double)>(log));
registration::method("log10",static_cast<long double(*)(long double)>(log10));
}
int main(int argc, char const *argv[])
{
string op;
long double n;
while(cin>>op)
{
cin>>n;
method m=type::get_global_method(op);
if(!m)
{
cout<<"Undefined"<<endl;
continue;
}
cout<<m.invoke({},n).get_value<long double>()<<endl;
}
return 0;
}
参考: