解析不规则的c++原型

Parsing irregular c++ prototypes

本文关键字:c++ 原型 不规则      更新时间:2023-10-16

我正在尝试构建一个解析和列出头文件内容的程序。到目前为止,一切顺利,我发现很容易解析和列出我编写的头文件,但是当我开始解析跨平台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是宏,因此可以简单地忽略它们,这是正确的方法吗?当然,这只是解析器在解析不同头文件时可能遇到的许多不规则变化之一,因此,任何处理解析任何"合法"头文件内容的通用技术都是受欢迎的。

在解析之前没有任何真正的替代方法可以扩展宏,至少如果您希望处理头文件具有与Microsoft相同的复杂性,或者与已经存在了10年或更长时间的编译器系统相关的任何其他头文件。

未预处理的源代码不是C;它只是未经预处理的源代码。宏(和你惊讶地没有提到的预预处理条件)可以以不是任意的但非常复杂的方式编辑明显的源代码。除非您也处理#include,否则您通常无法知道使用了什么宏,或者扩展了什么条件。

您可以让GCC为您做预处理器扩展,然后解析它。那就太远了这是最简单的方法

这仍然留下了解析实际C代码的问题,包括声明器的所有复杂性,以及片段(如 tx;)中的歧义,其中语句的含义取决于T的声明。要准确解析头文件,需要一个完整的C解析器。

我们的C前端可以做完整的预处理,或者你可以调用一种模式,在这种模式下,一些宏被展开,一些宏不被展开。通过调优此集,您通常可以解析这些头文件,而无需展开每个宏。预处理器条件要困难得多,因为它们可能出现在不方便的(非结构化的)地方。

如果您想要的只是函数的名称和签名,那么对宏进行简单的搜索和替换就足够了。

但是,您需要检查宏是否包含关键字(如返回值)。这可以通过在定义关键字时剥离每个关键字的宏定义来实现,但是跟踪它们并使用简单的预处理器是必要的。

与平台相关的关键字,如__declspec__attribute__,语法非常有限,而且只有少数几个,因此可以专门删除它们。

你可能想看看氧是如何处理这个的,因为它几乎完全是你想要的,并且处理宏。它允许按定义展开宏列表,也允许将宏列表扩展为自定义值。您可以将__declspec(x)扩展为零,并将所有其他扩展为默认的定义值。

这当然不是万无一失的,但是搜索和替换是你能得到的最简单的功能解决方案。您需要遵循标准的c++预处理器规则,这并不是非常复杂,使用额外的宏(const、declspec等)来剥离额外的属性,并解析最终结果。