python.clang AST解析:获取字段声明的C++模板参数

tss*_*sch 5 c++ python templates clang abstract-syntax-tree

我正在使用 clang 通过 python 接口提供的抽象语法树,尝试解析包含 std::vector 的简单结构:

#include <vector>

struct outer_t
{
    std::vector<int> vec_of_ints;
};
Run Code Online (Sandbox Code Playgroud)

我想获取向量的模板参数,但在 AST 的相应节点中找不到对它的引用。get_num_template_arguments() 成员函数返回 -1。因此我认为 get_template_* 函数不能使用。我尝试了以下方法:

import sys
import clang.cindex
clang.cindex.Config.set_library_file("/usr/lib/llvm-6.0/lib/libclang.so.1")

class Walker:
    def __init__(self, filename):
        self.filename = filename

    def walk(self, node):
        node_in_file =  bool(node.location.file and node.location.file.name == self.filename)
        if node_in_file:
            print(f"node.spelling = {node.spelling:14}, node.kind = {node.kind}")
            if node.kind == clang.cindex.CursorKind.TEMPLATE_REF:
                print(f"node.get_num_template_arguments = {node.get_num_template_arguments()}")
        for child in node.get_children():
            self.walk(child)

filename = sys.argv[1]
index = clang.cindex.Index.create()
translation_unit = index.parse(filename)

root = translation_unit.cursor        
walker = Walker(filename)
walker.walk(root)
Run Code Online (Sandbox Code Playgroud)

这会产生以下结果:

node.spelling = vec_of_ints   , node.kind = CursorKind.FIELD_DECL
node.spelling = std           , node.kind = CursorKind.NAMESPACE_REF
node.spelling = vector        , node.kind = CursorKind.TEMPLATE_REF
node.get_num_template_arguments = -1
Run Code Online (Sandbox Code Playgroud)

还有另一种方法来获取模板参数还是我做错了什么?

Sco*_*eak 3

游标TEMPLATE_REF类型似乎没有任何关于其参数的信息,至少对于这个例子来说是这样;我不知道为什么。但FIELD_DECL有一个type可以有模板参数。vec_of_ints以下是代码的最小修改版本,用于打印示例代码中字段的模板参数数量:

import sys
import clang.cindex
clang.cindex.Config.set_library_file("/usr/lib/llvm-6.0/lib/libclang.so.1")

class Walker:
    def __init__(self, filename):
        self.filename = filename

    def walk(self, node):
        node_in_file =  bool(node.location.file and node.location.file.name == self.filename)
        if node_in_file:
            print(f"node.spelling = {node.spelling:14}, node.kind = {node.kind}")
            # -------- BEGIN modified section --------
            type = node.type
            if type is not None:
                ntargs = type.get_num_template_arguments()
                if ntargs > 0:
                    print(f"  type.spelling = {type.spelling}")
                    print(f"  type.get_num_template_arguments = {ntargs}")
            # -------- END modified section --------
        for child in node.get_children():
            self.walk(child)

filename = sys.argv[1]
index = clang.cindex.Index.create()
translation_unit = index.parse(filename)

root = translation_unit.cursor
walker = Walker(filename)
walker.walk(root)
Run Code Online (Sandbox Code Playgroud)

当在示例输入文件上运行时,我得到:

node.spelling = outer_t       , node.kind = CursorKind.STRUCT_DECL
node.spelling = vec_of_ints   , node.kind = CursorKind.FIELD_DECL
  type.spelling = std::vector<int>
  type.get_num_template_arguments = 1
node.spelling = std           , node.kind = CursorKind.NAMESPACE_REF
node.spelling = vector        , node.kind = CursorKind.TEMPLATE_REF
Run Code Online (Sandbox Code Playgroud)

我并不声称这可以处理代码中出现的所有模板情况。我通过反复试验和阅读clang/cindex.py库模块源文件发现了上述内容。但它希望可以作为一个有用的起点。

无论如何,关于 Clang AST(以及几乎所有 C/C++ AST)需要理解的一件重要事情是类型不是主语法树中的节点。相反,类型是该树中某些节点的语义解释,因此有点放在一边。这就是为什么它们不作为 的参数出现walk