【问题标题】:clang python bindings: how to find type of a variableclang python绑定:如何查找变量的类型
【发布时间】:2018-02-12 15:54:13
【问题描述】:

我正在尝试使用 clang 及其 python 绑定来处理 C/C++ 代码的 AST。

我有以下测试 C++ 代码:

#include <stdio.h>

class myclass {
public:
        void mymethod() {
                printf("method\n");
        }
};

void testfunc() {
        myclass var;
        var.mymethod();
}

我编写了以下 python 代码(简化和缩短)来遍历它:

#! /usr/bin/python

import clang.cindex
import sys

def walk(node):
    if node.kind == clang.cindex.CursorKind.CALL_EXPR:
        print 'name: %s, type: %s' % (node.spelling or node.displayname, node.type.spelling)

    for c in node.get_children():
        walk(c)

index = clang.cindex.Index.create()
walk(index.parse(sys.argv[1]).cursor)

现在,当我的代码在 testfunc 中到达 var.mymethod() 时,它会显示“mymethod”和“void”。不是我所期望的。我正在尝试检索调用 mymethod 的类类型,而不是方法的返回类型。

【问题讨论】:

  • 您正在请求 call-expr 的“类型”(因此是对 mymethod 的调用的返回类型)。毫不奇怪,它是 void mymethod() - 我敢打赌,如果你将其更改为 int,它确实会显示为 int
  • 确实,你是对的!这就解释了。您还可以告诉我如何获取我正在调用该方法的类类型吗?这就是我想要实现的目标。
  • 或许能给你一个提示,但我不是走 AST 的专家。我的“专长”是为 LLVM 生成代码。我得先深入研究一下。

标签: python c++ clang


【解决方案1】:

如上面评论中所述,您将获得函数的返回类型。这不是调用 mymethod 的变量的类型。

查看AST输出(使用clang -Xclang -ast-dump -fno-diagnostics-color),这是testfunc的定义

`-FunctionDecl 0x5e14080 <line:9:1, line:12:1> line:9:6 testfunc 'void ()'
  `-CompoundStmt 0x5e14750 <col:17, line:12:1>
    |-DeclStmt 0x5e146b0 <line:10:9, col:20>
    | `-VarDecl 0x5e14130 <col:9, col:17> col:17 used var 'myclass' callinit
    |   `-CXXConstructExpr 0x5e14680 <col:17> 'myclass' 'void () noexcept'
    `-CXXMemberCallExpr 0x5e14728 <line:11:9, col:22> 'void'
      `-MemberExpr 0x5e146f0 <col:9, col:13> '<bound member function type>' .mymethod 0x5e13dd0
        `-DeclRefExpr 0x5e146c8 <col:9> 'myclass' lvalue Var 0x5e14130 'var' 'myclass'

然后您可以看到在CXXMemberCallExpr 内部,您有一个MemberExpr,在其中一个DeclRefExpr 引用回var 及其类型myclass。我不确定你究竟是如何用 Python 编写的,但通过从 CALL_EXPR 条目中转储一些内部结构应该不难弄清楚。

使用上面的代码,我将其修改为如下所示:

#! /usr/bin/python

import clang.cindex
import sys

def find_decl_ref_expr(node):
    for c in node.get_children():
        if c.kind == clang.cindex.CursorKind.DECL_REF_EXPR:
            print "Member function call via", c.type.spelling, c.displayname
        else:
            find_decl_ref_expr(c)


def called_from(node):
    for c in node.get_children():
        if c.kind == clang.cindex.CursorKind.MEMBER_REF_EXPR:
            find_decl_ref_expr(c);

def walk(node):
    if node.kind == clang.cindex.CursorKind.CALL_EXPR:
        print 'name: %s, type: %s' % (node.spelling or node.displayname, node.type.spelling)
    called_from(node)

    for c in node.get_children():
        walk(c)

index = clang.cindex.Index.create()
walk(index.parse(sys.argv[1]).cursor)

它有点工作,但它绝对不是一个完整的解决方案。例如,添加一个通过数组使用的指针也会打印用于进入数组的索引。为了完全理解复杂代码,我不确定您实际需要做什么 [例如如果myclass 是一个具有各种指针和索引操作的类的若干层]。

我还发布了一些我用来检查每个节点中的内容的代码:

def dump_children(node):
    for c in node.get_children():
        print c.kind, c.type.spelling
        dump_children(c)

【讨论】:

  • 这很难,它看起来与 C++ api 做事的方式非常不同(已经看了一段时间了)。所以如果有人想介入......?
  • 我只是从你的示例代码中倾倒了东西,看起来并没有太大的不同,我看看能不能做点什么。
  • 完美运行!谢谢!
猜你喜欢
  • 2011-02-07
  • 1970-01-01
  • 2014-12-25
  • 2010-09-28
  • 2023-03-07
  • 1970-01-01
  • 2016-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多