@encode 指令返回一个 const char *,它是传入的数据类型的各种元素的编码类型描述符。示例如下:
struct test
{ int ti ;
char tc ;
} ;
printf( "%s", @encode(struct test) ) ;
// returns "{test=ic}"
我可以看到使用 sizeof() 来确定原始类型 - 如果它是一个完整的对象,我可以使用类方法进行自省。
但是,它如何确定不透明结构的每个元素?
@encode 指令返回一个 const char *,它是传入的数据类型的各种元素的编码类型描述符。示例如下:
struct test
{ int ti ;
char tc ;
} ;
printf( "%s", @encode(struct test) ) ;
// returns "{test=ic}"
我可以看到使用 sizeof() 来确定原始类型 - 如果它是一个完整的对象,我可以使用类方法进行自省。
但是,它如何确定不透明结构的每个元素?
@Lothars 的回答可能是“愤世嫉俗的”,但不幸的是,它非常接近目标。为了实现类似的东西@encode()
,你需要一个完整的解析器来提取类型信息。好吧,至少对于“琐碎”@encode()
陈述(即,@encode(char *)
)以外的任何事情。现代编译器通常具有两个或三个主要组件:
前端必须解析所有源代码,并且基本上将源代码文本转换为内部的“机器可用”形式。
后端将内部的“机器可用”形式转换为可执行代码。
具有“中间端”的编译器通常这样做是因为某些需要:它们支持多个“前端”,可能由完全不同的语言组成。另一个原因是为了简化优化:所有优化过程都在相同的中间表示上工作。编译器套件是“三阶段”编译器的gcc
一个示例。 llvm
可以认为是“中间和后端”阶段的编译器:“低级虚拟机”是中间表示,所有优化都以这种形式进行。 llvm
也能够将它保持在这个中间表示直到最后一秒——这允许“链接时间优化”。编译器clang
真的是一个“前端”llvm
因此,如果您想向@encode()
“现有”编译器添加功能,您可能必须将其作为“源到源”“编译器/预处理器”。这就是最初的 Objective-C 和 C++ 编译器的编写方式——它们解析输入源文本并将其转换为“普通 C”,然后将其输入标准 C 编译器。有几种方法可以做到这一点:
yacc
andlex
组合一个 ANSI-C 解析器。你需要一个语法——ANSI C 语法 (Yacc)是一个好的开始。实际上,需要明确的是,当我说 时yacc
,我的意思是 野牛和flex
。而且,松散地,其他各种yacc
和lex
类似的基于 C 的工具:柠檬,dparser等...perl
与Yapp或EYapp 一起使用,它们yacc
是perl
. yacc
与基于 C 的想法相比,快速原型化想法可能更好lex
-perl
毕竟:正则表达式、关联数组、无内存管理等。注意:我没有使用这些工具中的任何一个来执行添加之类的任何操作的个人经验@encode()
,但我怀疑它们会很有帮助。
@Lothar 在他的评论中提出了一个很好的观点。我实际上打算包括lcc
,但看起来它在途中迷路了。
您可以通过首先实现 ANSI C 编译器来实现这一点,然后向其中添加一些实现特定的 pragma 和函数。
是的,我知道这是愤世嫉俗的答案,我接受反对票。
一种方法是编写一个预处理器,它读取类型定义的源代码,并将@encode
... 替换为相应的字符串文字。
如果您的程序是用 编译的-g
,另一种方法是编写一个函数,该函数在运行时从程序的调试信息中读取类型定义,或者使用gdb
或其他程序为您读取它,然后根据需要重新格式化它。该gdb
ptype
命令可用于打印特定类型的定义(或者如果这还不够,那么还有maint print type
,它肯定会打印出比您可能想要的更多的信息)。
如果您使用支持插件的编译器(例如 GCC 4.5),也可以为此编写编译器插件。然后,您的插件可以利用编译器已经解析的类型信息。显然,这种方法是非常特定于编译器的。