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

时间:2021-07-28
本文章向大家介绍C++实现反射---RTTR库的使用,主要包括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即可。
————————————————
版权声明:本文为CSDN博主「jianingshow」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jianingshow/article/details/52318413

原文地址:https://www.cnblogs.com/cnhk19/p/15070882.html