2010-02-12 221 views
7

我正在考虑为某些C++类添加某种反射功能(以便我不必使用RTTI):获取方法名称,声明字段,类名称......这类东西。你会如何在C++中实现基本的反射?

我在考虑解析现有的源文件,获取已声明字段列表&方法,并重写每个源文件,将这类信息添加到每个类。

您对这种方法有什么看法?我想从头开始做所有事情,因为我认为这是一个很好的学习机会。你会建议采取其他方式吗?

// OFFTOPIC:这是Qt如何做到的吗?

+0

这会不会成本比RTTI更那么这收获? – Mark 2010-02-12 15:34:01

+0

坚韧。您无法编组编译器从源代码生成您需要的元数据。 RTTI严重不足。你将不得不编写你自己的C++语言解析器。已经完成了...... – 2010-02-12 15:34:41

+0

@nobugz:当然,人们已经编写了自己的C++解析器。这远非微不足道。很简单,C++是一个真正的解析痛苦。 – 2010-02-12 15:47:05

回答

1

除非您想修改C++编译器或依赖于供应商扩展,否则您将需要一堆构建数据结构的CPP宏。

3

检查Boost.Mirror库。它尚未被Boost所接受,所以你必须从Boost Vault中获得download it

该库仍在开发中,文档很少,但通过研究provided examples,您可能可以实现您想要的功能。

编辑:如果你真的要分析自己的C++代码,那么也许你应该考虑clang

+0

哇。它看起来非常简洁并且易于使用。 – Eric 2010-02-12 16:05:18

+0

你是讽刺吗? – Manuel 2010-02-12 16:10:10

+2

print_meta_data Eric 2010-02-12 16:55:42

3

我叉GCC。

2

是的,这是Qt是如何做到的 - 除了不向类本身添加信息之外,它创建了一个名为元类的新类,其中包含有关该类的静态数据。这显然是更好的原因。

没有纯粹的C++方法将提供全自动反射 - 语言不允许它。有很多尝试,包括Boost.MirrorBoost.Reflection,但它们都需要样板添加到您的来源。

0

也许你可以使用typeinfo库,现在你可以在运行时使用对象类。例如:

#include<iostream> 

#include<typeinfo> 
class A{}; 

int main() 
{ 
A a; 

std::cout<<typeid(a).name(); 

} 

你可以看到更多: http://www.cplusplus.com/reference/std/typeinfo/

[]单曲

+0

我不想使用RTTI。 – Geo 2010-02-12 21:45:56

1

您可能会发现gccxml值得一看。它会(例如)将this转换为this,这意味着您只需解析XML而不是C++。

有一个有趣的SP &Ëpaper其描述了使用gccxml结合修饰的接头与“在一个干净的和非侵入性的方式提供的Java反射样功能到C++应用程序”。

1

改变编译器生成一个静态GetClass方法,返回一个指向描述该类的Class对象的指针,用于每个定义的类。

改变编译器/链接器来提取所需的元数据,并填充生成的可执行映像的特殊部分/符号中的元素 - 非常类似于添加了调试符号。

GetClass会使用(读取/加载/缓存?)上述元数据。

是的。这是很多工作。 (和一个很好的副作用是它让你几乎消除头文件 - 因为该信息现在可以从图像中拉出)

1

C++不支持反射。来自boost和RTTI的努力是相当有限的,并且只有一半。我不会推荐他们。

你提到你可以从scatch开始。那么你可以使用lex/yacc或ANTLR来编写C++词法分析器。相当多的工作,但你会学到很多东西。

如果编写解析器对您来说是一项艰巨的任务,还有其他一些选项可用于获取类的元数据。微软拥有提供符号信息的DIA SDK。 Doxygen可以生成XML文件,您可以解析该文件以获取类的字段名称和方法名称。我在解析doxygen XML文件方面取得了一些成功。

如果您只使用Microsoft平台,可行的解决方案是使用类型库(TLB)。它要求您将该类转换为MS IDL中的接口。 MIDL将IDL编译为.tlb,并且您的应用程序可以在运行时加载.tlb文件。您的应用程序可以通过ITypeInfo接口,属性和方法获取有关该类的几乎所有信息。

+0

哇! Doxygen甚至没有跨过我的脑海!酷一个! – Geo 2010-02-13 15:54:43

1

按照格林斯潘的第十法则,你很好去:只实施一个临时的,非正式指定的,错误缠身,缓慢实施一半的Common Lisp。选择一半支持实施反思。 :)

或者,这里有一个想法:

打开在构建地图文件(也可能是组装代)。实现一个生成代码的应用程序来创建一个动态库。您应该能够从源或其中一个构建工件获取关于类的所有静态信息(方法名称,参数,类型等)。使用映射或程序集,您应该能够找到函数地址,虚函数指针偏移量,全局变量地址,可变偏移量,关于堆栈和寄存器分配变量的信息等等。从这个信息构建一个库,其中包含通过传递类型名称,指针,函数名称等来获取所有这些信息的调用。

现在,在C++中编写一个包含函数和宏的库,允许您放置唯一的ID被编译成用于识别函数启动的代码,所有的反射调用,反射调用时的EIP偏移量等。在需要的地方使用内联汇编宏,如果需要的话,可以使用NOP来放置一些指令,通常在ID。此外,还提供了桥接库函数,这些函数将允许反射功能处理它可以内联的内容(比如获取函数的地址),而不必调用内置于后构建步骤的动态库。

最后,编写一个post-post构建反射链接器。我建议“kniltsoPostlink”,但这取决于你。在这一步中,搜索唯一的ID(是否提及简单,您应该可以创建ID GUID,以便可以在二进制文件中搜索它们),并且无论ID在何处标记对函数的反射调用,或者一个类的定义等等,在那里放置足够的数据(以一种格式,你可以在写入反射器库时及时地确定),然后在调用反射器库之前在ID中重写该调用,以便它会从这些位数据中提取需要的参数,或者只是将数据位置放在适当的位置,但我不能提前知道,但是当您编写它时,这些小细节只会出现在您身上。

无论如何,我知道我没有给出太多的代码,而且我实际上打算在有足够的空闲时间后在某个时候启动一个项目。我的意思是,每一个小部分都应该非常简单,以便逐步完成,只要遵循这里列出的指导原则,每个小部分都应该变得清晰。有可能其中一些实际上比我在这里描述的更简单,因为这是最坏的情况。你甚至可以不必重写代码到反射器调用;只需将数据放在适当的位置就可以让图书馆根据需要提取这些位,而无需进一步的信息。

我很高兴你问;我现在非常忙碌,但如果你有一个免费的夜晚,我想这将是第一个版本的一个好开始,我会很乐意尽快投入。

;)

+0

哇,我看到你问了这个问题已经一年半了,对不起,你必须长时间没有真正的答案,但很高兴知道我可以提供帮助。 :D♡ – shelleybutterfly 2011-08-19 21:28:48

+0

很有意思,也很详细。我会做一些阅读。谢谢! – Geo 2011-08-19 21:58:00

+0

好吧,不客气。 :)请注意,尽管我确实认为这可以起作用,但这只是一个路线图。 (换言之,关于它会变得多么容易的评论)。但我确实相信这一点,通过迭代处理来找到最好的解决方案,这是可以做到的。 (我也不太确定这比编写扩展到C++和扩展gcc来编译它更容易。)...听起来有点像我描述它的方式有趣:有点忘记C++,你是越来越体现你是否喜欢它...... :) – shelleybutterfly 2011-08-20 01:17:14