上一篇blog我阐述了一种实现非嵌入式的反射的基本思路。相比于通过宏和模板实现,这种非嵌入的反射的优点是不需要写额外的代码来记录meta信息。

首先,为了在c++中实现反射系统,我认为需要解决以下两个问题:

(1)根据一个给定符号,获取符号对应的地址信息。

(2)根据地址信息,能够对其进行相应操作。

对(2)需要再说明的是:为了能够对地址指向的对象进行操作,需要一些用于描述这个对象的最基本信息(比如对象的类型),而这些信息就是对象的meta信息。有了meta信息,我们才能正确的操作对象。

不同类型的对象的meta信息应该是不同的:

(1)对于简单的数据成员(int、double、struct等),meta信息应该包括数据所占内存的布局。通过内存布局和内存地址,我们可以对其进行get/set操作。

(2)对于函数,meta信息应该有调用惯例(Calling Conventions)、参数、返回值等信息。通过这些信息我们能对函数进行调用操作。

(3)对于类,meta信息可以认为是前两种的组合。应该包含方法表(函数集合)和数据成员集合。

暂时不考虑剩余的其他类型,如模板、宏等。

我以此实现了一个反射系统的demo。基本做法是,通过windows的dia(Debug Interface Access)api读取程序的PDB文件。以此来获得一个符号的地址信息,以及这个符号的基本meta信息。这样可以做到根据一个地址,对数据进行读写的操作。但是对函数来说似乎还不够。为了成功调用函数,我们还需要根据函数的调用惯例,正确的把参数入栈、并从指定位置获取返回值等。libffi就是专门干这事的,但是可惜的是它并不支持msvc:-(

不过好在我熟悉python,知道python也有ctype这个模块来处理ffi(Foreign Function Interface)。把它的源码稍作剪裁应该就可以拿来用了。这部分工作挺顺利的,浏览了一下ctype的实现,发现只需要改非常少的几处即可。cpython的libffi_msvc在此处:D

目前demo功能比较简陋,只实现了对数据对象的get/set,以及调用全局的函数。不过对于其他复杂的对象的反射,基本上参考这两个功能就可以轻松的实现了。

具体的使用如下代码所示:

1、加载程序对应的PDB文件

CReflMgr refl_mgr;

//从PDB文件加载反射信息
refl_mgr.LoadDataFromFile(g_wszPdbFileName);

2、对基本数据的get/set

//get instance data member
CReflObject *refl_obj_bar_c;
refl_mgr.GetReflObjectFromParent(refl_obj_bar, L"c", &refl_obj_bar_c); CReflValue refl_val_bar_c;
refl_mgr.GetClassInstDataMember(&bar, refl_obj_bar_c, &refl_val_bar_c);
printf("%d\n", refl_val_bar_c.m_Data.m_I8); //set instance data member
CReflObject *refl_obj_bar_i;
refl_mgr.GetReflObjectFromParent(refl_obj_bar, L"i", &refl_obj_bar_i); CReflValue refl_val_bar_i;
refl_val_bar_i.m_Data.m_I32 = ;
refl_mgr.SetClassInstDataMember(&bar, refl_obj_bar_i, &refl_val_bar_i);
printf("%d\n", bar.i);

3、调用函数

//函数
CReflObject *refl_obj_func1;
refl_mgr.GetReflObjectFromGlobal(L"TestCallFunc1", &refl_obj_func1);
refl_mgr.PrintReflObject(refl_obj_func1); std::vector<ffi_obj> vargs; ffi_obj arg1;
arg1.type = ffi_type_sint32;
arg1.value.i32 = ;
vargs.push_back(arg1); ffi_obj arg2;
arg2.type = ffi_type_pointer;
arg2.value.p = (void*)"hello world";
vargs.push_back(arg2); ffi_obj ret;
ret.type = ffi_type_void; refl_mgr.CallReflObject(refl_obj_func1, vargs, ret);

当然demo是不完整的,目前我认为还有就几个未解决的比较重要的问题是:

(1)处理对函数的不正确的调用。比如传入了错误的参数、返回值类型指定错了等。这部分cpython的libffi_msvc也没有做好,它做的仅仅是在windows下检测函数调用前后堆栈寄存器的位置是否一致,详见此处。但是仅有这个检测是不够的,它并不能有效的防止崩溃问题。我认为最好能做到能对参数、返回值进行类型检查,如果发现错误能打印错误,并且不进行函数的调用操作。

(2)如何在linux gcc环境下实现。

参考资料:

1、Reflection in C++ - a teaser

通过PDB文件实现非嵌入式的c++反射的更多相关文章

  1. PDB文件说明

    文/玄魂 .PDB文件,全称为“程序数据库”文件.我们使用它(更确切的说是看到它被应用)大多数场景是调试应用程序.目前我们对.PDB文件的普遍认知是它存储了被编译文件的调试信息,作为符号文件存在.那么 ...

  2. 每个开发人员必须知道PDB文件知识

    大多数开发人员都意识到PDB文件有助于您进行调试,但仅此而已.如果你不知道PDB文件是怎么回事,不要觉得很糟糕,因为虽然有文档在那里,但它分散在周围,而且大部分是为编译器和调试器编写器准备的.虽然编写 ...

  3. 关于 PDB 文件你需要知道什么?

    引言 大多数人知道 PDB 文件是用来帮助我们 debug 的,但也仅此而已. 本文主要介绍当你遇到 PDB 文件时(windows 开发中),你必须要知道的内容. 重要的事情说三遍 PDB 文件和源 ...

  4. Microsoft Visual Studio PDB文件相关事宜

    Microsoft Visual Studio PDB:调试的符号文件,程序数据库 (PDB) 文件保存着调试和项目状态信息,使用这些信息可以对程序的调试配置: 当以 /ZI 或 /Zi(用于 C/C ...

  5. Visual Studio 不生成.vshost.exe和.pdb文件的方法【转】

    Visual Studio 不生成.vshost.exe和.pdb文件的方法[转] 使用Visual Studio编译工程时,默认设置下,即使选择了「Release」时也会生成扩展名为「.vshost ...

  6. windbg不识别pdb文件符号

    一开始配置完毕后 输入reload  但不识别 输入reload -f 还是不识别 输入reload -f 模块名 继续不识别 !sym noisy 查看 输入reload 发现有了一堆的查找路径 把 ...

  7. Visual Studio无法查找或打开 PDB 文件解决办法

    Visual Studio无法查找或打开 PDB 文件解决办法 用VS调试程序时,有时会在VS底部的“输出”框中提示“无法查找或打开 PDB 文件”.这该怎么解决呢? 下面,我们以VS2013为例,来 ...

  8. .pdb文件的使用方法

    1.Demo1:用DLL_01生成my.dll.my.pdb.my.lib文件. 2.Demo2:在DLL_01_APP_02中使用DLL_01的dll. 步骤: 1.vs2008打开DLL_01_A ...

  9. 2016-07-07: 重新编译时vc90.pdb不是创建此预编译头时使用的pdb文件

    使用VS2008在一个解决方案中包含多个项目时,当设置多个项目的中间目录为同一个目录时,在增量编译时出现"重新编译时vc90.pdb不是创建此预编译头时使用的pdb文件,请重新创建预编译头问 ...

随机推荐

  1. C++模式学习------适配器模式

    适配器模式: 适配器模式属于结构型的设计模式,是将一个类的接口转换成使用方希望的另外一个接口,这样使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适配器模式有两种: 1.类的适配器:继承不 ...

  2. Qt——基本工具的使用

    本文主要介绍在windows系统中使用C++编写Qt程序所需要的一些工具,不会具体地讲工具怎么使用. 其它系统的安装本文不会涉及,在http://wiki.qt.io/Main中,有关于各种系统qt安 ...

  3. 利用Eric+Qt Designer编写倒计时时钟

    [前言]前几日通过编写命令行通讯录,掌握了Python的基本语法结构,于是开始向更高水平冲击,利用Eric与Qt Designer 编写一个带界面的小程序.本次实操中也确实遇到了不少问题,通过学习也都 ...

  4. 【刷题】BZOJ 3529 [Sdoi2014]数表

    Description 有一张n×m的数表,其第i行第j列(1<=i<=n,1<=j<=m)的数值为能同时整除i和j的所有自然数之和.给定a,计算数表中不大于a的数之和. In ...

  5. [AT2567] [arc074_c] RGB Sequence

    题目链接 AtCoder:https://arc074.contest.atcoder.jp/tasks/arc074_c 洛谷:https://www.luogu.org/problemnew/sh ...

  6. 【CF472G】Design Tutorial: Increase the Constraints

    Description 给出两个01序列\(A\)和\(B\) 要求回答\(q\)个询问每次询问\(A\)和\(B\)中两个长度为\(len\)的子串的哈明距离 ​ 哈明距离的值即有多少个位置不相等 ...

  7. BZOJ1495 [NOI2006]网络收费 【树形dp + 状压dp】

    题目链接 BZOJ1495 题解 观察表格,实际上就是分\(A\)多和\(B\)两种情况,分别对应每个点选\(A\)权值或者\(B\)权值,所以成对的权值可以分到每个点上 所以每个非叶节点实际对应一个 ...

  8. 洛谷 P2731 骑马修栅栏 Riding the Fences 解题报告

    P2731 骑马修栅栏 Riding the Fences 题目背景 Farmer John每年有很多栅栏要修理.他总是骑着马穿过每一个栅栏并修复它破损的地方. 题目描述 John是一个与其他农民一样 ...

  9. 面向对象高级编程(2)-使用@property

    使用@property 在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9999 这显然不合逻 ...

  10. Java考试题之十

    QUESTION 230 Given: 10. class One { 11. public One foo() { return this; } 12. } 13. class Two extend ...