C中的reflection支持

我知道这不被支持,但我想知道是否有任何技巧。 任何提示?

总的来说,反思是程序分析某些代码结构的手段。 此分析用于更改代码的有效行为。

反思作为分析一般是非常薄弱的​​; 通常它只能提供对函数和字段名称的访问。 这个缺点来自语言实现者,实际上不希望在运行时使完整的源代码可用,以及适当的分析例程来从源代码中提取出想要的东西。

另一种方法是通过使用强大的程序分析工具来解决程序分析问题,例如,可以按照编译器的方式精确地parsing源文本。 (通常人们build议滥用编译器本身来做到这一点,但通常这是行不通的,编译器机器想成为一个编译器,很难将其用于其他目的)。

需要的是一个工具:

  • parsing语言源文本
  • 构build代表程序的每个细节的抽象语法树。 (如果AST保留源代码布局的注释和其他细节(如列号,文字基数值等)
  • 构build符号表,显示每个标识符的范围和含义
  • 可以从函数中提取控制stream
  • 可以从代码中提取数据stream
  • 可以为系统构build一个调用图
  • 可以确定每个指针指向什么
  • 使用上述事实可以构build自定义分析器
  • 可以根据这种自定义分析来转换代码(通常通过修改代表parsing代码的AST)
  • 可以从修订后的AST中重新生成源文本(包括布局和注释)。

使用这样的机制,可以在需要的任何细节层次上实现分析,然后转换代码以达到运行时reflection所能达到的效果。 有几个主要的好处:

  • 分析的详细程度或数量是一个野心的问题(例如,它不受运行时reflection只能做什么的限制)
  • 没有任何运行时间开销来实现反映的行为变化
  • 涉及的机制可以是通用的,并且可以跨越多种语言应用,而不仅限于特定语言实现提供的内容。
  • 这与C / C ++的想法是兼容的,你不支付你不使用的东西。 如果你不需要反思,你不需要这个机器。 而你的语言不需要有内置的弱思考的智力包袱。

请参阅我们的DMS Software Reengineering Toolkit ,了解可用于C,Java和COBOL以及C ++的大部分function的系统。

围绕它的任何技巧? 任何提示?

编译器可能会有select地生成“debugging符号文件”,debugging器可以用它来帮助debugging代码。 链接器也可以生成一个“映射文件”。

技巧/技巧可能是生成并读取这些文件。

基于对如何将reflection添加到C ++应用程序的响应? (堆栈溢出)和C ++被认为是C的“超集”的事实,我会说你倒霉了。

还有一个关于为什么C ++没有reflection(Stack Overflow)的很好的答案。

我需要在C ++项目中的一堆struct进行reflection。
我用所有这些结构的描述创build了一个xml文件 – 幸运的是这些字段types是原始types。
我使用了一个模板 (不是C ++ template )来自动生成每个structclass以及setter / getter方法。
在每个class我使用一个映射关联string名称和类成员(指向成员)。

我并不后悔使用反思,因为它开辟了新的方法来devise我的核心function,而这些function是我无法想象的。
(顺便说一句,这是一个使用原始数据库的程序的外部报告生成器)

所以,我使用代码生成,函数指针和地图来模拟reflection。

你需要从头开始实施。 在C语言中,结构和复合types没有任何运行时信息。 元数据根本不存在于标准中。

  1. 为C实现reflection会简单得多…因为C是简单的语言。
  2. 有一些基本的选项可用于alazing程序,如通过调用dlopen / dlsym来检测函数是否存在 – 取决于您的需要。
  3. 有创build代码的工具,可以使用tcc修改/扩展自己的代码。
  4. 您可以使用上述工具来创build您自己的代码分析器。

技巧和窍门总是存在。 看看Metaresc图书馆https://github.com/alexanderchuranov/Metaresc

它提供了types声明的接口,也将为types生成元数据。 基于元数据,您可以轻松地序列化/反序列化任何复杂的对象。 开箱即用,您可以序列化/反序列化XML,JSON,XDR,Lisp-like符号,C-init符号。

这是一个简单的例子:

 #include <stdio.h> #include <stdlib.h> #include <math.h> #include "metaresc.h" TYPEDEF_STRUCT (point_t, double x, double y ); int main (int argc, char * argv[]) { point_t point = { .x = M_PI, .y = M_E, }; char * str = MR_SAVE_XML (point_t, &point); if (str) { printf ("%s\n", str); free (str); } return (EXIT_SUCCESS); } 

这个程序将输出

 $ ./point <?xml version="1.0"?> <point> <x>3.1415926535897931</x> <y>2.7182818284590451</y> </point> 

图书馆工作正常最新的海湾合作委员会和铛。

我知道以下选项,但都是成本和很多限制:

  • 使用libdl#include <dfcln.h>
  • 调用像objdumpnm的工具
  • parsing对象文件(使用相应的库)
  • 包含parsing器并在编译时生成必要的信息。
  • “滥用”链接器来生成符号数组。

我将使用一些unit testing框架作为示例,因为unit testing框架的自动testing发现是reflection非常方便的一个典型示例,也是大多数C的unit testing框架所不具备的。

使用libdl#include <dfcln.h> )(POSIX)

如果你在POSIX环境中,可以使用libdl来做一点反思。 插件是以这种方式开发的。

使用

 #include <dfcln.h> 

在您的源代码中,并链接到-ldl

然后,您可以访问函数dlopen()dlerror()dlsym()dlclose() ,您可以使用它们在运行时加载和访问/运行共享对象。 但是,它不能让您轻松访问符号表。

这种方法的另一个缺点是,你基本上限制reflection到加载为dynamic库的对象(在运行时通过dlopen()加载的共享对象)。

运行nmobjdump

您可以运行nmobjdump来显示符号表并parsing输出。 对我来说, nm -P --defined-only -g xyz.o给出了很好的结果,parsing输出也很简单。 你只会对每一行的第一个单词感兴趣,它们是符号名称,也许是第二个单词,即单元types。

如果你不知道对象的名称,也就是说对象实际上是一个共享对象,那么至less在Linux上你可能会忽略以'_'开始的符号名称。

objdumpnm或类似的工具也经常在POSIX环境之外使用。

自己parsing对象文件

你可以自己parsing对象文件。 你可能不想从头开始实现,但使用现有的库。 这是nmobjdump甚至libdl如何实现的。 你可以看看nmobjdumplibdl的源代码以及它们使用的库,以了解它们是如何做的。

涉及parsing器

您可以编写一个parsing器和代码生成器,它在编译时生成必要的reflection信息,并将其存储在目标文件中。 那么你有很大的自由,甚至可以实现原始forms的注释。 这就是像AceUnit这样的unit testing框架。

我发现编写一个涵盖直接C语法的parsing器是相当简单的。 编写一个真正理解C并能处理所有情况的parsing器并不是微不足道的。 所以,这有一些限制,取决于你想要反思的C语法多么奇特。

“滥用”链接器来生成符号数组

你可以把你想要reflection的符号引用到一个特殊的部分,并使用链接器configuration来发出节的边界,这样你就可以用C来访问它们。

我在这里描述了C中的N-dependency injection – 比链接器定义的数组更好的方法? 这是如何工作的。

但要小心,这取决于很多事情,不是很便携。 我只用GCC / ld试过,我知道它不适用于所有的编译器/链接器。 此外,几乎可以保证死代码消除不会检测到你如何调用这个东西,所以如果你使用死代码消除,你将不得不添加所有reflection的符号作为入口点。

陷阱

对于某些机制, 死代码消除可能是一个问题,特别是当您“滥用”链接器来生成符号数组时。 可以通过将reflection的符号作为链接器的入口点来解决,根据符号的数量,这可能既不好也不方便。

结论

结合nmlibdl实际上可以给出相当好的结果。 这个组合可以和Java中的JUnit 3.x使用的Reflection的级别几乎一样强大。 给出的reflection级别足以为C实现一个JUnit 3.x风格的unit testing框架,包括通过命名约定进行testing用例发现。

涉及parsing器是更多的工作,并限于你自己编译的对象,但给你最大的权力和自由。 给定的reflection级别足以为C实现JUnit 4.x风格的unit testing框架,包括通过注释进行testing用例发现。 AceUnit是C的一个unit testing框架,完全是这样的。

结合parsing和链接器来生成符号数组可以给出非常好的结果 – 如果你的环境在你的控制之下,你可以确保使用这种方式工作的链接器。

当然,你可以结合所有的方法来缝合零件,直到它们适合你的需求。

只要制作任何types的键控表,树,链表或任何你喜欢pipe理或发现有效的集合。 添加一个键是否string,types/ ID组合或什么都不指定,并将地址指向一个函数或结构。 一个超级天真版本的reflection可以是以下的一个集合:

 struct reflectable{ size_t size,id,type; // describes payload char* name; void* payload; } 

用一个大的ol开关的情况下,你做每个types或名称或macros附加相同的处理程序。 或者,总是附加函数,这些函数是您的可reflection结构中的任何东西的接收者,它们与处理程序相同,但有更多的调度模型直接位于您的容器上。