我正在尝试构建一个解析和列出头文件内容的程序。到目前为止,这么好,我发现它很容易解析和列出我写的标题,但是当我开始解析跨平台API标题时,事情变得混乱。
我目前的方法相当简单,这是解析以下函数的伪代码示例:
void foo(int a);
void is a type, so we are dealing with instancing a type
foo is the name of that type
foo is followed by brackets, meaning it is a function of type void named foo
int is a type...
a is the name of that type instance
foo is a function of type void that takes one parameter of type int named a
然而,当我进入更大更复杂的标题时,我偶然发现了一些不规则的原型,涉及宏,而且神知道什么。一个例子:
GLAPI void APIENTRY glEvalCoord1d( GLdouble u );
GLAPI和APIENTRY是平台相关的宏。哪种方式破坏了我的简单解析方案,因为它希望对象的名称遵循其类型。这两个宏碰巧转换为__stdcall,__ declspec(dllimport)或extern,但理论上它们可能意味着什么,在编译时它们的含义不清楚。
如何编写我的解析器以便它可以处理这些场景而不会混淆?宏本身是在较早阶段定义的,因此解析器可以知道GLAPI和APIENTRY是宏,因此可以简单地忽略它们,这是要走的路吗?当然,这只是解析器在解析不同标头时偶然发现的不规则变化的众多变化之一,因此欢迎任何处理任何“合法”标题内容解析的一般技术。
答案 0 :(得分:1)
在解析之前没有任何真正的扩展宏的替代方法,至少如果你想要与Microsoft的复杂性相同的进程头文件,或者与编译器系统相关的任何其他头文件已经存在了10年或更多。
未经处理的源代码不是C;它只是未经处理的源代码。宏(以及您提到的预处理器条件没有提及)可以以非任意但非常复杂的方式编辑适当的源。除非你同时处理#includes,否则你不能经常知道宏使用了什么,或条件扩展了。
您可以让GCC为您进行预处理器扩展,然后解析它。那会很远 最简单的方法。
这仍然存在解析真实C代码的问题,具有声明者的所有复杂性,以及片段中的歧义,例如 TX; ,其中语句的含义取决于T的声明。解析准确地说,你需要一个完整的C解析器。
我们的C Front End可以进行全面的预处理,或者你可以调用一些扩展某些宏的模式,有些则不是。通过调整此集合,您通常会解析此类标头而不会扩展每个宏。预处理器条件要困难得多,因为它们可能发生在不方便(非结构化)的地方。
答案 1 :(得分:0)
如果你想要的只是函数的名称和签名,那么对宏进行简单的搜索和替换就足够了。
但是,您需要检查宏是否包含关键字(如返回值)。这可能是通过在定义的时候去除每个但关键字的宏定义来实现的,但是跟踪它们并使用简单的预处理器是必要的。
平台相关关键字(例如__declspec
和__attribute__
的语法非常有限,并且只有少数几个,因此具体删除它们是可能的。
您可能想看一下doxygen如何处理这个问题,因为它几乎完全符合您的要求并处理宏。它允许按定义扩展宏列表,以及应扩展为自定义值的宏列表。您可以调整它以将__declspec(x)
扩展为空,并默认将所有其他值扩展为其定义的值。
这当然不是万无一失的,但搜索和替换是关于您将获得的最简单的功能解决方案。您需要遵循标准的C ++预处理程序规则,这些规则并不是非常复杂,需要额外的宏(const,declspec等)来删除额外的属性,并解析最终结果。