8

@encode 指令返回一个 const char *,它是传入的数据类型的各种元素的编码类型描述符。示例如下:

struct test
{ int ti ;
  char tc ;
} ;

printf( "%s", @encode(struct test) ) ;
// returns "{test=ic}"

我可以看到使用 sizeof() 来确定原始类型 - 如果它是一个完整的对象,我可以使用类方法进行自省。

但是,它如何确定不透明结构的每个元素?

4

3 回答 3

15

@Lothars 的回答可能是“愤世嫉俗的”,但不幸的是,它非常接近目标。为了实现类似的东西@encode(),你需要一个完整的解析器来提取类型信息。好吧,至少对于“琐碎”@encode()陈述(即,@encode(char *))以外的任何事情。现代编译器通常具有两个或三个主要组件:

  • 前端。
  • 中间端(对于某些编译器)。
  • 后端。

前端必须解析所有源代码,并且基本上将源代码文本转换为内部的“机器可用”形式。

后端将内部的“机器可用”形式转换为可执行代码。

具有“中间端”的编译器通常这样做是因为某些需要:它们支持多个“前端”,可能由完全不同的语言组成。另一个原因是为了简化优化:所有优化过程都在相同的中间表示上工作。编译器套件是“三阶段”编译器的gcc一个示例。 llvm可以认为是“中间和后端”阶段的编译器:“低级虚拟机”是中间表示,所有优化都以这种形式进行。 llvm也能够将它保持在这个中间表示直到最后一秒——这允许“链接时间优化”。编译器clang真的是一个“前端”llvm

因此,如果您想向@encode()“现有”编译器添加功能,您可能必须将其作为“源到源”“编译器/预处理器”。这就是最初的 Objective-C 和 C++ 编译器的编写方式——它们解析输入源文本并将其转换为“普通 C”,然后将其输入标准 C 编译器。有几种方法可以做到这一点:

自己滚

  • 使用yaccandlex组合一个 ANSI-C 解析器。你需要一个语法——ANSI C 语法 (Yacc)是一个好的开始。实际上,需要明确的是,当我说 时yacc,我的意思是 野牛flex。而且,松散地,其他各种yacclex类似的基于 C 的工具:柠檬dparser等...
  • perlYappEYapp 一起使用,它们yaccperl. yacc与基于 C 的想法相比,快速原型化想法可能更好lex-perl毕竟:正则表达式、关联数组、无内存管理等。
  • 使用Antlr构建您的解析器。我对这个工具链没有任何经验,但它是另一个(似乎)更适合 Java 开发人员的“编译器编译器”工具。似乎有免费可用的 C 和 Objective-C 语法可用。

破解另一个工具

注意:我没有使用这些工具中的任何一个来执行添加之类的任何操作的个人经验@encode(),但我怀疑它们会很有帮助。

  • CIL - 没有使用此工具的个人经验,但设计用于解析 C 源代码,然后用它“做事”。根据我从文档中收集到的信息,该工具应该允许您提取所需的类型信息。
  • 稀疏- 值得一看,但不确定。
  • clang - 没有将其用于此目的,但据称其中一个目标是使其“易于破解”以用于此类东西。特别是(同样,没有个人经验)在进行所有解析的“繁重”工作时,让您专注于“有趣”的部分,在这种情况下将提取上下文和语法敏感的类型信息,然后将其转换为到一个普通的 C 字符串。
  • gcc 插件- 插件是 gcc 4.5(这是编译器的当前 alpha/beta 版本)功能,“可能”允许您轻松连接到编译器以提取您需要的类型信息。不知道插件架构是否允许这种事情。

其他

  • Coccinelle - 最近将此加入书签以“稍后查看”。这个“可能”能够做你想做的事情,而“可能”能够不费吹灰之力地做到这一点。
  • MetaC - 最近也收藏了这个。不知道这会有多大用处。
  • mygcc - “可能”做你想做的事。这是一个有趣的想法,但它并不直接适用于你想要的。来自网页:“Mygcc 允许程序员添加考虑到语法、控制流和数据流信息的自己的检查。”

链接。

编辑#1,奖励链接。

@Lothar 在他的评论中提出了一个很好的观点。我实际上打算包括lcc,但看起来它在途中迷路了。

  • lcc - lccC 编译器。这是一个特别小的 C 编译器,至少在源代码大小方面是这样。它还有一本书,我强烈推荐。
  • tcc - tccC 编译器。不像 教育学那么好lcc,但绝对值得一看。
  • poc - pocObjective-C 编译器。这是一个“源到源”的 Objective-C 编译器。它解析 Objective-C 源代码并发出 C 源代码,然后将其传递给gcc(嗯,通常是gcc)。有许多 Objective-C 扩展/特性在gcc. 绝对值得一看。
于 2010-02-14T01:47:52.527 回答
4

您可以通过首先实现 ANSI C 编译器来实现这一点,然后向其中添加一些实现特定的 pragma 和函数。

是的,我知道这是愤世嫉俗的答案,我接受反对票。

于 2010-02-13T11:18:21.370 回答
2

一种方法是编写一个预处理器,它读取类型定义的源代码,并将@encode... 替换为相应的字符串文字。

如果您的程序是用 编译的-g,另一种方法是编写一个函数,该函数在运行时从程序的调试信息中读取类型定义,或者使用gdb或其他程序为您读取它,然后根据需要重新格式化它。该gdb ptype命令可用于打印特定类型的定义(或者如果这还不够,那么还有maint print type,它肯定会打印出比您可能想要的更多的信息)。

如果您使用支持插件的编译器(例如 GCC 4.5),也可以为此编写编译器插件。然后,您的插件可以利用编译器已经解析的类型信息。显然,这种方法是非常特定于编译器的。

于 2010-02-12T23:23:42.100 回答