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?
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:
CXX_BASE_SPECIFIER kindTEMPLATE_REF)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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With