在RecursiveASTVisitor
的文档中,没有名称为VisitNamedDecl,VisitCXXRecordDecl的虚拟方法。等。 那么如何称呼它们自动调用呢?
有两件事使设计RecursiveASTVisitor
难以理解:
-
它使用 奇怪的重复出现的模板模式 (CRTP)调度到用户提供的方法,而不是更多的 使用C++
virtual
的传统(但可能更慢)技术 方法。 -
绝大多数可重写的方法都是使用 有点复杂的宏系统和多包含的头文件, 后者由构建过程中的特殊工具生成。 后果之一是 氧气,产生 您(和我)链接到的已发布文档看不到它们。
调度设计在 详细说明 部分,但我将提供我自己的解释 填写一些缺失的详细信息。
由于第二个问题,我将使用预处理器的片段 输出。 为了具体性,我正在预处理deleted-ctor.cc
,我最近发布的代码 这个答案,但是 任何预处理形式的RecursiveASTVisitor.h
都应与 我正在展示的部分。
访客调度机制
让我们专注于VisitNamedDecl
因为它在 问题。 模板类定义,经过预处理(以及一些 使用indent
缩进 工具和其他小的编辑以提高可读性),看起来像:
template <typename Derived>
class RecursiveASTVisitor {
public:
...
Derived &getDerived() { return *static_cast<Derived *>(this); }
...
bool TraverseDecl(Decl *D);
bool TraverseLabelDecl(LabelDecl *D);
...
bool WalkUpFromDecl(Decl *D)
{
return getDerived().VisitDecl(D);
}
bool WalkUpFromNamedDecl(NamedDecl *D)
{
if (!getDerived().WalkUpFromDecl(D))
return false;
if (!getDerived().VisitNamedDecl(D))
return false;
return true;
}
bool VisitNamedDecl(NamedDecl *D)
{
return true;
}
bool WalkUpFromLabelDecl(LabelDecl *D)
{
if (!getDerived().WalkUpFromNamedDecl(D))
return false;
if (!getDerived().VisitLabelDecl(D))
return false;
return true;
}
...
};
Traverse
方法是入口点。 他们称之为WalkUpFrom
方法,它们"走上"C++类层次结构和 为 AST 节点所在的类调用Visit
方法 问题就是一个例子。
getDerived
方法是 CRTP 机制的关键要素。 由 在其上调用方法,而不是直接this
定义的任何方法 在Derived
将(静态地)覆盖相应的基类 方法。
超类TraverseDecl
方法执行基于以下 其参数的类型:
template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D)
{
...
switch (D->getKind()) {
...
case Decl::Label:
if (!getDerived().TraverseLabelDecl(static_cast<LabelDecl *>(D)))
return false;
break;
...
}
return true;
}
我选择专注于LabelDecl
因为它是从抽象NamedDecl
派生出来的具体类。 它的Traverse
方法如下所示:
template <typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseLabelDecl(LabelDecl *D)
{
bool ShouldVisitChildren = true;
bool ReturnValue = true;
if (!getDerived().shouldTraversePostOrder())
if (!getDerived().WalkUpFromLabelDecl(D))
return false;
if (ReturnValue && ShouldVisitChildren)
if (!getDerived().TraverseDeclContextHelper(dyn_cast<DeclContext>(D)))
return false;
if (ReturnValue) {
for (auto *I : D->attrs())
if (!getDerived().getDerived().TraverseAttr(I))
return false;
}
if (ReturnValue && getDerived().shouldTraversePostOrder())
if (!getDerived().WalkUpFromLabelDecl(D))
return false;
return ReturnValue;
}
这个想法是它在当前节点上调用WalkUpFrom
,并Traverse
在子节点上。 有两个调用WalkUpFrom
因为 访客可以在预购或后购模式下操作。 再 所有调用都经过getDerived
,因此可以覆盖。
宏元编程系统
回到RecursiveASTVisitor
的定义,第一个 类的一部分直接在RecursiveASTVisitor.h
.
但是然后我们到了头文件的这一部分,它声明了Decl
层次结构的Traverse
方法:
// ---- Methods on Decls ----
// Declare Traverse*() for all concrete Decl classes.
#define ABSTRACT_DECL(DECL)
#define DECL(CLASS, BASE) bool Traverse##CLASS##Decl(CLASS##Decl *D);
#include "clang/AST/DeclNodes.inc"
// The above header #undefs ABSTRACT_DECL and DECL upon exit.
DeclNodes.inc
是在构建过程中生成的文件DeclNodes.td
使用名为 TableGen 的工具。 在DeclNodes.inc
中,我们发现(除许多其他事项外):
#ifndef NAMED
# define NAMED(Type, Base) DECL(Type, Base)
#endif
ABSTRACT_DECL(NAMED(Named, Decl))
...
#ifndef LABEL
# define LABEL(Type, Base) NAMED(Type, Base)
#endif
LABEL(Label, NamedDecl)
鉴于ABSTRACT_DECL
和DECL
的定义似乎只是 在#include
之前,我们看到ABSTRACT_DECL(NAMED(Named, Decl))
扩展为无,并且:
LABEL(Label, NamedDecl) expands to
NAMED(Label, NamedDecl) which expands to
DECL(Label, NamedDecl) which expands to
bool TraverseLabelDecl(LabelDecl *D);
接下来RecursiveASTVisitor.h
我们将:
// Define WalkUpFrom*() and empty Visit*() for all Decl classes.
bool WalkUpFromDecl(Decl *D) { return getDerived().VisitDecl(D); }
bool VisitDecl(Decl *D) { return true; }
#define DECL(CLASS, BASE)
bool WalkUpFrom##CLASS##Decl(CLASS##Decl *D) {
TRY_TO(WalkUpFrom##BASE(D));
TRY_TO(Visit##CLASS##Decl(D));
return true;
}
bool Visit##CLASS##Decl(CLASS##Decl *D) { return true; }
#include "clang/AST/DeclNodes.inc"
在具体定义了WalkUpFromDecl
和VisitDecl
之后,这定义了 子类的WalkUpFrom
和Visit
。 我不会经历所有的 这些的宏扩展详细信息。
最后,TraverseLabelDecl
来自此宏:
// This macro makes available a variable D, the passed-in decl.
#define DEF_TRAVERSE_DECL(DECL, CODE)
template <typename Derived>
bool RecursiveASTVisitor<Derived>::Traverse##DECL(DECL *D) {
bool ShouldVisitChildren = true;
bool ReturnValue = true;
if (!getDerived().shouldTraversePostOrder())
TRY_TO(WalkUpFrom##DECL(D));
{ CODE; }
if (ReturnValue && ShouldVisitChildren)
TRY_TO(TraverseDeclContextHelper(dyn_cast<DeclContext>(D)));
if (ReturnValue) {
/* Visit any attributes attached to this declaration. */
for (auto *I : D->attrs())
TRY_TO(getDerived().TraverseAttr(I));
}
if (ReturnValue && getDerived().shouldTraversePostOrder())
TRY_TO(WalkUpFrom##DECL(D));
return ReturnValue;
}
以及它的使用:
DEF_TRAVERSE_DECL(LabelDecl, {// There is no code in a LabelDecl.
})
其他Traverse
方法在大括号内有(手写)代码 在子节点上调用Traverse
。
所有这些机制的结果是编译器看到Traverse
,WalkUpFrom
和所有 AST 类的Visit
方法,其中大部分 自动生成,但Doxygen看不到大部分。