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


在这里插入图片描述

使用过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_object和rttr::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_object
World::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即可。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你可以使用反射技术来获取类成员中的属性并以字符串方式输出。反射是一种在运行时检查、访问和修改程序元素的能力。在C++11中,可以使用一些实现反射,例如Boost.Reflection和RTTR(Run 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是一个第三方,需要在项目中添加相应的头文件和链接。如果不想使用第三方,也可以手动实现反射功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【网络星空】

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值