前言
什么是高阶宏?高阶宏就是以宏为参数的宏。以宏为参数的宏这就是这篇文章的重点。其实业界领先的代码库是有很多值得我们学习的,研究开源库也可以是大家学习的方向哦,通常开源库中90%的内容超出了新手对于编程知识的认知。如果你有耐心去研究,我相信一定有助于大家学会一些聪明技巧。而高阶宏也广泛应用于很多的开源库中。
问题描述
C ++是用于定义抽象的功能强大的语言,可让您摆脱冗余。函数和方法处理命令性代码的重复块。基类使您可以重用数据定义。模板可以让您几乎处理任何类型的数据。
即便如此,仍然有很多重复的现象似乎无法消除。例如,假设我们正在使用一种语言的语法。通常,解析器会生成AST(抽象语法树),然后将其传递给编译器。编译器使用Ye Olde Visitor Patterne遍历 AST(抽象语法树) 并为其生成一些较低层的表示形式。
根据您语言的丰富程度,您将有很多不同的AST(抽象语法树)类来表示不同的语法元素:文字,一元运算符,中缀表达式,语句,流控制,定义等。例如,V8(javescript的一个引擎)有40个类涵盖您可以用JavaScript表达的所有内容。这些是相对简单的类型。简化的外观有点像以下代码:
想想像这样的三十多个类,我们可以用C ++简化这些定义本身。每个类都足够不同,以至于只写出来就最简单,最清晰。真正乏味的地方是使用这些类的所有周围代码。为了使编译器可以根据不同的AST类轻松分派给不同的代码,通常需要定义一个类,如下所示:
该代码确实只是重复的样板。还有更多的代码并没有写出来。对每种AST节点类型也都有一个枚举很有用,这样我们也可以switch直接对节点的类型进行访问,而不必经历访问者的所有访问。因此,您需要以下内容:
对于调试,也可以方便地获取AST(抽象语法树)节点类型的字符串表示形式:
C ++通常的抽象在这里对我们无济于事:在所有这些情况下,重复都在某种类型定义或语句的中间。实际上,C ++的设计仅允许您抽象整个语句(通过创建函数)或类型(通过创建模板或基类)。
高阶宏的使用
C和C ++有一个非常有意思的东西,它不能对实际的语法元素造成任何伤害:预处理。它甚至不知道声明是什么!它只是看到大量的文本。这正是我们所需要的。在我们所有的问题示例中,我们都希望能够:“对于每种AST(抽象语法树)节点类型,插入此代码块,但在某些地方插入节点的类名。”接下来我们尝试这样做:
#define DEFINE_VISIT(type) virtual void visit##type(type* expr) = 0
这里使用到了## 连接操作符,不清楚的可以看看以往的文章,这样,我们可以将访问者类简化为:
其实与以上代码相比较并没有得到什么优化,但这对于每个问题领域都是不同的。我们需要它采用一个参数,该参数是我们为每种类型生成的代码块:
当我们使用该AST_NODE_LIST宏时,该code参数将是什么?它必须是在预处理时可用的东西,可以带有参数,并且可以生成大量代码。那只留下一个答案:一个宏。使用AST_NODE_LIST优化我们刚刚定义访问者类,即可以优化为:
当AST_NODE_LIST展开后,将展开到DEFINE_VISIT每个AST节点类型。然后,这些反过来将扩大到定义该类游客的方法。同样,我们的枚举变为:
这就是全部。最后,将其转换为字符串的函数:
请注意,我们在这里使用字符串化和标记粘贴,不仅是字面地替代AST节点类的名称,而且还将其用作字符串,或从中构建一个更大的名称,这摆脱了一些乏味的代码,还有另一个不错的作用。如果以后需要添加新的节点类,只需将其添加到中AST_NODE_LIST,使用的每个地方都会自动将其捡起。这样,您就不必去修改AstVisitor,AstType或者typeString()。
结束语
文章都是手打原创,每天最浅显的介绍C语言、C++,windows知识,喜欢我的文章就关注一波吧,每天带你学习C/C++不同的知识,也可以看到最新更新和之前发表的文章哦。如果你基础差,可以关注下通俗易懂,深入浅出,一个视频只讲一个知识点。视频不深奥,不需要钻研,在公交、在地铁、在厕所都可以观看,随时随地涨姿势