使用过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

C++实现反射---RTTR库的使用的更多相关文章

  1. AspectCore.Extension.Reflection : .NET Core反射扩展库

    在从零实现AOP的过程中,难免会需要大量反射相关的操作,虽然在.net 4.5+/.net core中反射的性能有了大幅的优化,但为了追求极致性能,自己实现了部分反射的替代方案,包括构造器调用.方法调 ...

  2. c++ 反射类型

    来自: 实现代码=== // // Created by lizhen on 2017/9/29. // #ifndef BOOST_ALL_CALLBACKFUNCTION_H #define BO ...

  3. Java编程的逻辑 (84) - 反射

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  4. Android 插件化开发(一):Java 反射技术介绍

    写在前面:学习插件化开发推荐书籍<Android 插件化开发指南>,本系列博客所整理知识部分内容出自此书. 在之前的项目架构的博文中,我们提到了项目插件化架构,提到插件化架构不得不提的到J ...

  5. Qt 如何使用反射?

    Qt 如何使用反射? c++ 反射 标准库暂时还没有,那我们来看看如何使用 qt 来进行反射. 反射类的案例 1. 通过注册的类型需找 id 进行实例化该类 myclass.h #include &l ...

  6. C#反射的实现

    一,什么是反射? 1,System.Reflection 命名空间中的类与 System.Type 使你能够获取有关加载的程序集和其中定义的类型的信息,如类.接口和值类型. 可以使用反射在运行时创建. ...

  7. 别在重复造轮子了,几个值得应用到项目中的 Java 开源库送给你

    我是风筝,公众号「古时的风筝」.文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面.公众号回复『666』获取高清大图. 风筝我作为一个野路子开发者,直到 ...

  8. robotframework笔记22

    创建测试库 支持的编程语言 机器人框架本身是用写的 Python 和自然的测试 库扩展它可以使用相同的实现 语言. 运行时框架上 Jython ,图书馆也可以 实现使用 Java . 纯Python代 ...

  9. Android Weekly Notes Issue #254

    Android Weekly Issue #254 April 23rd, 2017 Android Weekly Issue #254 本期内容包括: 如何用Kotlin写一个Gradle Plug ...

随机推荐

  1. 妹子始终没搞懂OAuth2.0,今天整合Spring Cloud Security 一次说明白!

    大家好,我是不才陈某~ 周二发了Spring Security 系列第一篇文章,有妹子留言说看了很多文章,始终没明白OAuth2.0,这次陈某花了两天时间,整理了OAuth2.0相关的知识,结合认证授 ...

  2. 【Plink】Error: Multiple instances of '_' in sample ID.?

    目录 前言 原因 解决方法 方法一:修改样本名 方法二:修改--id-delim 方法三:加入--double_id或--const-fid参数 前言 将vcf转化为plink格式时,命令如下: pl ...

  3. 【基因组注释】RepeatMasker和RepeatModeler安装、配置与运行避坑

    目录 1.conda安装 2.配置RepBase 3.RepeatMasker避坑 4.RepeatProteinMask避坑 5.RepeatModeler避坑 6.自定义重复序列库 后记 1.co ...

  4. Perl语言编程(大骆驼)

    啰嗦几句 Perl的时代已经过去,现在年轻的同事们基本上都在用Python了.但个人认为单就生物信息文本处理而言,Perl语言是绝对够用的.最主要的是,前辈们搭建的流程大多数是Perl写的,因此,如果 ...

  5. [转载]ORA-02287: 此处不允许序号

    原文地址:ORA-02287: 此处不允许序号作者:nowhill 转载自 http://blog.sina.com.cn/s/blog_6d496bad01011dyv.html 开发人员反映序列不 ...

  6. tensoboard [Errno 22] Invalid argument 以及 Invalid format string问题解决

    Invalid argument 问题解决: 需要保证tensorboard与tensorflow版本一致. Invalid format string 问题解决: 修改 manager.py 文件中 ...

  7. [云原生]Docker - 安装&卸载

    目录 系统要求 卸载旧版本 安装Docker 方法一:通过repo安装 设置Repository 安装Docker Engine 升级Docker Engine 方法二:通过package安装 方法三 ...

  8. day05文件编辑命令

    day05文件编辑命令 mv命令:移动文件 mv命令:mv命令用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中. 格式:mv [原来的文件路径] [现在的文件路径] mv命令后面既可以跟 ...

  9. centos服务器上挂载exFat U盘

    有些场景,我们需要在服务器上插入U盘,但是现在的U盘或者移动硬盘,大多都是exFat格式的,有时候linux系统识别不了,可以按照以下方式挂载. 1.安装nux repo(可以不装) yum inst ...

  10. 零基础学习java------23---------动态代理,ip,url案例

    1. 动态代理 2. ip,url案例 给定的access.log是电信运营商的用户上网数据,第一个字段是时间, 第二个字段是ip地址,第三个字段是访问的网站,其他字段可以忽略不计. 第一个字段是网段 ...