我可以从cppyy得到AST吗

Can I get the AST from cppyy

我想在创建 python 绑定之前访问 cppyy 中的 AST。我想用它来生成其他类型的绑定。

我看过cppyy-generator,但是需要在机器上单独安装clang。由于 cppyy 无需单独安装 clang 即可进行 JIT 编译,因此我不得不相信 AST 可从底层的 cling 解释器获得。有没有办法从 cppyy 获取这个 AST 信息?

示例:

import cppyy

cppyy.cppdef("""
namespace foo
{
class Bar
{
public:
    void DoSomething() {}
};
}

""")

cppyy 可以(令人惊讶地)为我生成 cppyy.gbl.foo.Bar。这意味着它必须使用 Cling 进行编译,获取 AST,并生成 python。如何查看 AST 数据?

谢谢!

编辑:

我可以看到我需要的大部分信息都在 cppyy-backend capi 和 cpp_cppyy 文件中。但是,我的 CPython foo 不够强大,无法弄清楚如何调用它们以及如何从 python 脚本访问它们。

编辑2:

目前我们正在使用 castxml 和 pygccxml 的组合来生成表示 AST 的 python 数据结构。我看到与 cppyy 有很多重叠,并希望减少对 cppyy 的依赖,因为我们已经将它用于其他事情,而且它很好地独立。

我们将 AST 数据用于多种用途。一个重要的是代码生成。所以我们想多次迭代 AST like you can with pygccxml.

这里有一些歧义 b/c 相同的名称适用于不同的步骤和不同的地方。让我解释一下结构(和历史),这甚至可以回答你的问题。

cppyy-generator 使用 Clang Python 绑定。因此,它访问的 AST 是 C++ 的,并且它以其完整(丑陋)的荣耀可用。您不需要 cppyy 的任何部分来使用 Clang Python 绑定。 cppyy-generator 服务于您希望所有本地 C++ 实体 pre-loaded 进入 Python 模块的特定用例。由于 cppyy 使用惰性的一切和 auto-loading,出于性能原因,“所有 C++ 实体”(本地或其他)的概念没有 well-defined 的含义。所以用了libclang,概念很清楚。

cppyy-backend capi(或C-API)是在reductio 中开发的API,用于服务cppyy 的PyPy 实现。它是 C-style API 到 bootstrap cppyy/C++。它被简化为编写 Python-C++ 绑定的基本要素,隐藏了许多与 Clang AST 无关的细节(例如,模板可以存在于 Clang AST 中的 15 种左右的方式被简化为“IsTemplate”等) .后端 C-API 不以任何方式依赖或使用 Python。

后端 C-API 的实现相当 non-pretty。部分是因为历史原因(坏事),部分是为了隐藏所有 Cling 和 Clang,以防止与可能使用 Clang 或 LLVM 的应用程序的其他部分发生冲突(好事;正在使用的 Clang 版本) by Cling 是定制的,可能不适用于例如 Numba)。同样,所有这些都完全独立于 Python.

然后,它在Python中的使用。有两种不同的实现:CPyCppyy for CPython,用C实现,PyPy _cppyy模块,用RPython实现。两者都执行从 Python 通过 C-API 进入 C++ 的咒语。既不生成也不使用 Python AST:都直接生成和操作 Python 实体。这发生得很懒惰。考虑以下步骤:在上面的示例中,Python 用户将键入类似 cppyy.gbl.foo.Bar().DoSomething() 的内容。在cppyy中,Python的__getattr__是用来截取names的,然后就简单的通过后台给Cling询问是否知道什么是fooBar等等。例如,C-API GetScope("foo") 将 return 一个有效的标识符,所以 CPyCppyy/_cppyy 知道生成一个 Python class 来表示命名空间.然而,它绝不会完整地扫描 AST 中的全局(甚至 foo)命名空间以先验地生成绑定。根据您的描述,CPyCppyy/_cppyy中没有对您有用的内容。

回到你的第一个声明,你想要生成其他类型的绑定。您没有说明绑定的类型,但是选择 C-API 的主要原因是它位于 Cling 之上,而不是直接来自 C++ 或通过其 [=92= 作为 Clang AST 的 Clang ] 绑定将是。 Cling 提供对 JIT 的轻松访问,但您也可以直接从 Clang(它的库,而不是 AST)对其进行编程。作为这种 easyu 访问的示例,在后端 C-API,您可以将要 JITted 的 C++ 字符串转储到 compile 函数中(它与 cppdef 中的函数完全相同你的例子)。 Cling 人员计划直接从 Cling 为动态语言提供更好的接口,但这是一项正在进行的工作,目前尚不可用。

最后,请注意 Cling 包含 Clang,因此如果您安装 Cling,您仍然会得到 Clang(和 LLVM),这可能是一个严重的依赖关系。

编辑:从根本上说,与其他工具相反,cppyy 不提供起点列表(例如“所有 classes”),也不full/true AST。您可以从后端复制 cpp_cppyy.h header(它不是安装的一部分),只需包含它并使用它(所有符号都已导出),但您需要知道先验 classes 的列表。示例:

import cppyy

cppyy.cppdef('#define RPY_EXPORTED extern')
cppyy.include('cpp_cppyy.h')

import cppyy

cppyy.cppdef("""
namespace foo {
class Bar {
public:
    void DoSomething() {}
};
}""")

cpp = cppyy.gbl
capi = cpp.Cppyy

scope_id = capi.GetScope(cpp.foo.Bar.__cpp_name__) # need to know existence
for i in range(capi.GetNumMethods(scope_id)):
     m = capi.GetMethod(scope_id, i)
     print(capi.GetMethodName(m))

但是如您所见,它不提供原始代码的 one-to-one 结果。例如,所有 compiler-generated 构造函数和析构函数都列为方法。

在后端 API 中也没有像 run_functions = unittests.member_functions('run') 那样的东西,就像您在 link 中的 pygccxml 文档一样。原因是这样在 cppyy 的上下文中没有任何意义。例如。如果另一个 header 加载了更多 run 函数怎么办?如果它是一个模板化函数并且弹出更多实例怎么办?如果在后面的代码中出现 using namespace ...,引入更多的 run 重载怎么办?

cppyy 确实具有 GetAllCppNames C-API 功能,但不能保证详尽无遗。它的存在是为了 tab-completion 在代码编辑器中的好处(它在绑定范围的自定义 __dir__ 函数中被调用)。其实,正是因为不完整,才cppyy-generator使用了libclang。

你在评论中提到了 gInterpreter,但那是我提到 ea 的历史的一部分lier:它是 libclang 提供的完整 AST 和 Python 所需的极简主义(例如后端 C-API)之间的 ill-fated 中间体。是的,你可以直接使用它(事实上,它仍然在后端下面使用C-API),但是它笨重得多,而且没有什么好处。

例如,要处理“获取所有 'run' 方法”示例,您可以这样做:

import cppyy

cppyy.cppdef("""
namespace foo {
void run(int) {}
void run(double) {}
}""")

cpp = cppyy.gbl

# using the ROOT/meta interface
cls = cpp.CppyyLegacy.TClass.GetClass(cpp.foo.__cpp_name__)
print('num "run" overloads:"', cls.GetListOfMethodOverloads('run').GetSize())

# directly through gInterpreter
gInterp = cpp.gInterpreter

cls = gInterp.ClassInfo_Factory(cpp.foo.__cpp_name__)
v = cpp.std.vector['const void*']()
gInterp.GetFunctionOverloads(cls, 'run', v)
gInterp.ClassInfo_Delete(cls)

print('num "run" overloads:"', len(v))

但以前的界面(通过 CppyyLegacy.TClass)可能不会保留,gInterpreter 真的很丑,如您所见。

我敢肯定,您不会乐意尝试让 cppyy 取代 pygccxml 的使用,如果我是您,我会改用 Clang Python 绑定。