zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    electron调用c#动态库
    Mybatis使用自定义类型转换Postgresql
    Spring Boot Security And JSON Web Token
    从零开始把项目发布到NPM仓库中心
    从零开始把项目发布到Nuget仓库中心
    从零开始把项目发布到maven仓库中心
    vue项目中如何在外部js中例如utils.js直接调用vue实例及vue上挂在的方法
    vue单页应用在页面刷新时保留状态数据的方法
    Vue watch 监听复杂对象变化,oldvalue 和 newValue 一致的解决办法。
    vue项目的登录跳转代码
  • 原文地址:https://www.cnblogs.com/cnhk19/p/15070882.html
Copyright © 2011-2022 走看看