使用过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. namp相关命令大全

    常用功能: -探测主机存活- 扫描端口- 探测主机操作系统信息- 检测漏洞 nmap 常用的几个参数 nmap -v ip 显示详细的扫描过程 nmap -p  ip  扫描指定端口 nmap -A ...

  2. Codeforces 587F - Duff is Mad(根号分治+AC 自动机+树状数组)

    题面传送门 第一眼看成了 CF547E-- 话说 CF587F 和 CF547E 出题人一样欸--还有另一道 AC 自动机的题 CF696D 也是这位名叫 PrinceOfPersia 的出题人出的- ...

  3. CF 786 E ALT

    CF 786 E ALT 一个居民有两个选择:分配一只宠物,路上都有宠物 一个守卫有两种选择:分配一只宠物,不分配宠物 我们找一个原点,到每个居民都有一条边,表示是否给他宠物 从每个居民向他路上的守卫 ...

  4. go变量、类的概念以及类的使用方式,嵌套结构体

    go变量.类的概念以及类的使用方式,嵌套结构体 Go变量 go使用var声明变量,当声明变量时,这个变量对应的值总是会被初始化.这个值要么用指定的值初始化,要么用零值(即变 量类型的默认值)做初始化. ...

  5. Hive-insert into table 与 insert overwrite table 区别

    区分insert into 和 insert overowrite: 0. 命令格式 INSERT OVERWRITE|INTO TABLE tablename [PARTITION (partcol ...

  6. 17.Power of Four-Leetcode

    #define IMIN numeric_limits<int>::min() #define IMAX numeric_limits<int>::max() class So ...

  7. PDFium 渲染

    PDFium 是 Chromium 的 PDF 渲染引擎,许可协议为 BSD 3-Clause.不同于 Mozilla 基于 HTML5 的 PDF.js,PDFium 是基于 Foxit Softw ...

  8. 用python写的推箱子搜索程序

    1 # -*- coding: gbk -*- 2 from functools import reduce 3 from copy import deepcopy 4 import re 5 def ...

  9. 【Netty】最透彻的Netty原理架构解析

    这可能是目前最透彻的Netty原理架构解析 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件.整体架构,知其然且知其所以然,希望给大家在实际开发实践.学习开源项目方面提供参考. ...

  10. HelloWorldMBean

    package mbeanTest; public interface HelloWorldMBean { public String getHello(); public void setHello ...