Может ли libclang проанализировать шаблон CRTP?

Я пытаюсь использовать 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_CXXBaseSpecifier, но он дает мне только вид CXCursor_ClassDecl.

Если Base не был классом шаблона, libclang найдет CXCursor_CXXBaseSpecifier.

Я хочу найти классы, которые наследуются от Base, но это невозможно, когда libclang дает только вид ClassDecl. "Публичная база" не дает курсора, она игнорируется.

Кто-нибудь знает как это решить?

1 ответ

Решение

Курсоры, которые имеют вид CXX_BASE_SPECIFIER будут дочерние курсоры, которые позволят вам определить эту информацию. В тех случаях, когда базовый спецификатор ссылается на шаблонный класс, он будет иметь два дочерних узла (типа) TEMPLATE_REF и TYPE_REF. Вы можете использовать эту информацию в узле TEMPLATE_REF для сравнения с курсором шаблона класса.

Чтобы было понятнее, я покажу небольшой пример. Довольно печатать (libclang) версию AST следующего:

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++ (для stackru), я представлю версию 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
Другие вопросы по тегам