libclang represents the C++ Abstract Syntax Tree (AST) using the concept of Cursors (CXCursor). Each cursor represents a specific node in the AST (such as a namespace, class, function, or variable) that corresponds to a piece of source code.
Key characteristics of the libclang approach:
- One-to-one mapping: There is a strong relationship between the AST nodes and the source code syntax.
- Callback-based traversal: libclang primarily accesses a node's children using a callback mechanism (e.g.,
VisitChildren). This means you cannot easily traverse the tree in a single pass or perform multiple passes without re-traversing or manually caching the data. - Complexity: While basic cursors are straightforward, higher complexity is found in
Stmt (statements) and Exprs (expressions).
Because of the callback-based nature, tools that require multiple passes over the AST often need to implement an intermediate data layer to decouple the tool from libclang's native AST.
// Example of the callback-based traversal pattern used in libclang
private static void PrintASTByCursor(CXCursor cursor, int level, List<string> saveList)
{
bool needPrintChild = true;
saveList.Add(GetOneCursorDetails(cursor, level, out needPrintChild));
unsafe
{
PrintCursorInfo cursorInfo = new PrintCursorInfo();
cursorInfo.Level = level + 1;
cursorInfo.SaveList = saveList;
GCHandle cursorInfoHandle = GCHle.Alloc(cursorInfo);
// Accessing children requires a callback (VisitorForPrint)
cursor.VisitChildren(VisitorForPrint,
new CXClientData((IntPtr)cursorInfoHandle));
}
}