C++实现反射---RTTR库的使用

来源:互联网 发布:linux 运行lua脚本 编辑:程序博客网 时间:2024/06/05 06:49

使用过C#或者Java 的童鞋,应该对这些语言提供的反射机制有所了解。所谓反射,在我看来就是在只知道一个类的名字(字符串形式)的情况下,自动创建出具体的类实例,并且能够枚举该类型拥有的属性、方法等信息。使用反射写出来的代码可以做到异常的精致简洁。

由于我们最近开发的产品使用的是C++语言,然而这种语言并没有内置反射这种机制。于是从网上进行了调研,发现了一些不错的提供C++反射支持的库,如CPP-Reflection、Vlpp、ponder等。我们的产品是用VS2013开发的,对C++11的支持不够完善,这些库一般要用VS2015才能编译。最终我选择了一个叫做RTTR的开源库(Github地址:RTTR传送门),有兴趣的童鞋可以自行编译,这里我提供了使用VS2013编译的x64和x86预编译包。

下面用一个简短的示例演示该库的用法:

Person.h

#include <rttr/type>namespace World{class Person{public:Person();~Person();void set_name(const std::string& name);const std::string& get_name() const;void set_age(int age);int get_age();virtual void show();void growupTo(int age=20);private:std::string m_name;int m_age;RTTR_ENABLE()};}
Person.cpp

#include "Person.h"#include <rttr/registration>#include <iostream>namespace World{RTTR_REGISTRATION{rttr::registration::class_<Person>("World::Person").constructor<>()(rttr::policy::ctor::as_std_shared_ptr).property("name", &Person::get_name, &Person::set_name).property("age", &Person::get_age, &Person::set_age).method("show", &Person::show).method("growupTo", &Person::growupTo)(rttr::default_arguments(18),rttr::parameter_names("age"));}Person::Person():m_age(0){}Person::~Person(){}void Person::set_name(const std::string& name){m_name = name;}const std::string& Person::get_name() const{return m_name;}void Person::set_age(int age){m_age = age;}int Person::get_age(){return m_age;}void Person::show(){std::cout << "我的名字是: " << m_name << ", 我今年" << m_age << "岁" << std::endl;}void Person::growupTo(int age/* =20 */){m_age = age;std::cout << m_name << "长到了: " << m_age << "岁" << std::endl;}}

main.cpp

#include <rttr/type>#include <iostream>int _tmain(int argc, _TCHAR* argv[]){rttr::type t = rttr::type::get_by_name("World::Person");rttr::variant var = t.create();rttr::property prop = t.get_property("name");prop.set_value(var, std::string("小明"));prop = t.get_property("age");prop.set_value(var, 18);rttr::method meth = t.get_method("show");meth.invoke(var);std::cout << "属性: " << std::endl;for (auto& prop : t.get_properties()){std::cout << "属性名: " << prop.get_name() << ", 属性类性: " << prop.get_type().get_name() << std::endl;}std::cout << "方法: " << std::endl;for (auto& meth : t.get_methods()){std::cout << "方法名称: " << meth.get_name() << ", 方法签名: " << meth.get_signature() << std::endl;for (auto& info : meth.get_parameter_infos()){std::cout << "方法参数下标: " << info.get_index() << ", 参数名" << info.get_name() << std::endl;}}getchar();return 0;}

可以看到,main.cpp中并没有引用Person.h,但却创建出了Person的实例。

有时可能需要将RTTR中的variant转成具体的某个类,可以看到在Person.cpp中的RTTR_REGISTRATION块中,对Person类的构造函数用了rttr::policy::ctor::as_std_shared_ptr的描述,可选的还有rttr::policy::ctor::as_objectrttr::policy::ctor::as_raw_ptr。这三种情况下,代码的书写方式都不一样,详细的可以参见RTTR的官方教程。下面给出各种情况下的转换写法:

//as_shared_ptr//std::shared_ptr<World::Person> person = var.get_value<std::shared_ptr<World::Person>>();//as_raw_ptr//World::Person* person = var.get_value<World::Person*>();//as_objectWorld::Person person = var.get_value<World::Person>();
当然,此时必须要包含Person.h了。

注:若项目编译失败,报了类似error LNK2001: unresolved external symbol "public: static struct rttr::detail::as_object const rttr::policy::ctor::as_object" (?as_object@ctor@policy@rttr@@2U0detail@3@B)的错,需要增加一个预编译宏:RTTR_DLL


----------------------------------------------我是分割线--------------------------------------------------

在实际使用过程中又发现了一些需要注意的问题(一些坑)。我们的项目结构是:首先编译一堆静态链接库(lib),在最终的exe中链接这些文件。有两个问题:

1.这些lib之间也存在引用关系,假设rrtr在lib1中使用,rttr2引用了rtt1,那么在exe中若链接lib1和lib2,若lib2没有定义RTTR_DLL预编译宏的话,会报一个很奇怪的链接错误,因此lib2也需要在项目设置中增加RTTR_DLL

2.假设在lib1中的class1使用了rttr,然后exe链接lib1,若exe中的所有参与编译的cpp中都没有使用过class1类(包括定义临时变量、全局变量或new一个指针),在根据类名动态创建类时会失败(rttr::type::get_by_name("World::Person"))。我的解决方法是随便找一个参与编译的cpp文件,在文件开头定义一个全局的class1即可。

0 0
原创粉丝点击