libclang 可以解析 CRTP 模式吗?
can libclang parse the CRTP pattern?
我正在尝试使用 libclang 来解析 C++,但它似乎与 CRTP 模式有关,即当 class 继承自使用派生的 class 实例化的模板时:
// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
// ...
};
我想让 libclang 找到游标种类 CXCursor_CXXBaseSpecifier,但它只给我 CXCursor_ClassDecl 种类。
如果 Base 不是模板 class,libclang 将找到 CXCursor_CXXBaseSpecifier。
我想要完成的是找到class继承自Base的es,但是当libclang只给出ClassDecl种类时这是不可能的。 'public Base'没有给游标,好像是忽略了。
有人知道怎么解决吗?
具有 CXX_BASE_SPECIFIER
类型的游标将具有 child 游标,可让您确定此信息。在基本说明符引用模板 class 的情况下,它将有两个 child 节点(同类)TEMPLATE_REF 和 TYPE_REF。您可以在 TEMPLATE_REF 节点中使用该信息与 class 模板游标进行比较。
为了更清楚地说明这一点,我将展示一个小例子。漂亮地打印以下 AST 的 (libclang) 版本:
template<class T>
class Base { };
class X1 : public Base<X1> {};
class Y1 {};
class X2 : public Y1 {};
给出:
TRANSLATION_UNIT tmp.cpp
+--CLASS_TEMPLATE Base
| +--TEMPLATE_TYPE_PARAMETER T
+--CLASS_DECL X1
| +--CXX_BASE_SPECIFIER Base<class X1>
| +--TEMPLATE_REF Base
| +--TYPE_REF class X1
+--CLASS_DECL Y1
+--CLASS_DECL X2
+--CXX_BASE_SPECIFIER class Y1
+--TYPE_REF class Y1
所以一个基本的方法是:
- 每个 class
- 找到所有 children
CXX_BASE_SPECIFIER
kind - 对于基本节点,发现所有这些节点都有两个 children(其中一个是
TEMPLATE_REF
类型) - 对于
TEMPLATE_REF
个节点,检查它们是否与感兴趣的 class 模板有共同的定义。
鉴于这将是 C/C++(对于 Whosebug)中的一段非常大的代码,我将展示一个实现这些步骤的 Python 2 版本,这应该是相当易于翻译。
import clang
from clang.cindex import CursorKind
def find_template_class(name):
for c in tu.cursor.walk_preorder():
if (c.kind == CursorKind.CLASS_TEMPLATE) and (c.spelling == name):
return c
def inherits_from_template_class(node, base):
for c in node.get_children():
if c.kind != CursorKind.CXX_BASE_SPECIFIER:
continue
children = list(c.get_children())
if len(children) != 2:
continue
if children[0].kind != CursorKind.TEMPLATE_REF:
continue
ctd = children[0].get_definition()
if ctd == base:
return True
return False
idx = clang.cindex.Index.create()
tu = idx.parse('tmp.cpp', unsaved_files=[('tmp.cpp', s)], args='-xc++'.split())
base = find_template_class('Base')
for c in tu.cursor.walk_preorder():
if CursorKind.CLASS_DECL != c.kind:
continue
if inherits_from_template_class(c, base):
print c.spelling