可以解析CRTP模式

can libclang parse the CRTP pattern?

本文关键字:模式 CRTP 可以解      更新时间:2023-10-16

我正在尝试使用libclang来解析C ,但是CRTP模式似乎存在问题,即当类从派生类实例化的模板中继承时:

// 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_cxxxbasespecifier,但它只给了我cxcursor_classdecl。

如果基本不是模板类,则libclang会找到cxcursor_cxxxbasespecifier。

我想完成的是找到从基础继承的类,但是当libclang只提供classDecl种类时,这是不可能的。"公共基础"没有给出光标,似乎被忽略了。

有人知道如何解决这个问题吗?

具有一种CXX_BASE_SPECIFIER的光标将具有子光标,使您可以确定此信息。如果基本说明符引用模板类,则它将具有两个子节点(类型)template_ref和type_ref。您可以在template_ref节点中使用该信息与类模板光标进行比较。

为了更清楚,我将举一个小例子。漂亮打印以下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

所以一种基本方法是:

  1. 每个班级
  2. 找到所有有CXX_BASE_SPECIFIER种类的孩子
  3. 对于基本节点,找到所有这些都有两个孩子(其中一个是TEMPLATE_REF
  4. 对于TEMPLATE_REF节点,请检查它们是否具有感兴趣类模板的共同定义。

鉴于这将是C/C (对于stackoverflow)中的一个很大的代码,我将提供一个实现这些步骤的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