Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

can libclang parse the CRTP pattern?

Tags:

c++

crtp

libclang

I am trying to use libclang for parsing C++, but it seems to have problem with the CRTP pattern, i.e when a class inherits from a template that is instantiated with the derived 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>
{
  // ...
};

I want libclang to find the cursor kind CXCursor_CXXBaseSpecifier, but it only gives me the CXCursor_ClassDecl kind.

If Base was not a template class, libclang will find the CXCursor_CXXBaseSpecifier.

What I want to accomplish is to find classes that inherits from Base, but it is not possible when libclang only gives the ClassDecl kind. The 'public Base' does not give a cursor, it seems to be ignored.

Does anyone know how to solve this?

like image 368
user2479653 Avatar asked Dec 09 '25 09:12

user2479653


1 Answers

Cursors that have a kind of CXX_BASE_SPECIFIER will have child cursors which allow you to determine this information. In cases where the base specifier refers to a template class, it will have two child nodes (of kind) TEMPLATE_REF and TYPE_REF. You can use that information in the TEMPLATE_REF node to compare against a class template cursor.

To make this clearer, I'll show a small example. Pretty printing the (libclang) version of the AST of the following:

template<class T>
class Base { };
class X1 : public Base<X1> {};
class Y1 {};
class X2 : public Y1 {};

Gives:

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

So a basic approach is:

  1. For each class
  2. Find all it's children that have CXX_BASE_SPECIFIER kind
  3. For the base nodes, find all of those have two children (and one of those is has the kind TEMPLATE_REF)
  4. For the TEMPLATE_REF nodes, check if they have a common definition with the class template of interest.

Given that this would be a very big piece of code in C/C++ (for stackoverflow), I'll present a Python 2 version that implements those steps, that should be fairly easy to translate.

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
like image 80
Andrew Walker Avatar answered Dec 11 '25 01:12

Andrew Walker



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!