我能从cppyy得到AST吗

2024-05-16 16:27:00 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我见过cppyy发电机,但它需要在机器上单独安装叮当声。由于cppyy可以在不单独安装clang的情况下进行JIT编译,因此我不得不相信AST可以从底层的cling解释器获得。有没有办法从cppyy获取此AST信息

例如:

import cppyy

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

""")

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

谢谢

编辑

我可以看到我需要的很多信息都在cppyy后端capi和cpp_cppyy文件中。然而,我的cpythonfoo不够强大,无法弄清楚如何调用它们,以及如何从python脚本访问它们

Edit2

目前,我们使用castxml和pygccxml的组合来生成表示AST的python数据结构。我看到了很多与cppyy的重叠之处,并且希望将依赖项仅减少到cppyy,因为我们已经将其用于其他事情,并且它是非常独立的

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


Tags: 数据机器信息类型foobar情况ast
1条回答
网友
1楼 · 发布于 2024-05-16 16:27:00

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

cppyy生成器使用了铿锵的Python绑定。因此,AST访问是C++的,它可以在其完全(丑陋)的荣耀中获得。使用铿锵Python绑定不需要cppyy的任何部分。CPPYY生成器提供了一个特定的用例,在这里您希望所有本地C++实体都预加载到Python模块中。由于CPPYY利用了惰性的一切和自动加载,出于性能原因,“所有C++实体”(本地或其他)的概念没有明确定义的含义。因此,在概念清晰的地方,使用了libclang

cppyy后端capi(或C-API)是在ReduceTio中开发的API,用于为cppyy的PyPy实现提供服务。它是引导cppyy/C++的C风格API。编写Python-C++绑定就成了它的基本任务,隐藏了许多与clangast无关的细节(例如,clangast中模板存在的15种左右的方式被简化为“IsTemplate”等)。后端C-API根本不依赖或使用Python

后端C-API的实现相当不美观。一部分是因为历史原因(一件坏事),另一部分是为了隐藏所有的Clang和Clang,以防止与可能使用Clang或LLVM的应用程序的其他部分发生冲突(一件好事;Clang使用的Clang版本是定制的,可能不适用于例如NUBA)。同样,所有这些都与Python无关

然后,介绍它在Python中的使用。有两种不同的实现:CPyCppyy for CPython(用C实现)和PyPy_cppyy模块(用RPython实现)。两者都执行通过C API从Python到C++的咒语。既不生成也不使用Python AST:都直接生成和操作Python实体。这是懒惰的。仔细考虑以下步骤:在上面的示例中,Python用户将键入类似cppyy.gbl.foo.Bar().DoSomething()的内容。在cppyy中,Python的__getattr__被用来截取名称,然后它只需通过后端来询问它是否知道什么是fooBar等等。例如,C-API GetScope("foo")将返回一个有效标识符,因此CPyCppyy/U cppyy知道生成一个Python类来表示名称空间。但是,它在任何时候都不会完全扫描AST中的全局(甚至是foo)名称空间,以预先生成绑定。根据您的描述,CPyCppyy/_cppyy中没有对您有用的内容

回到您的第一条语句,您希望生成其他类型的绑定。你不陈述什么类型的绑定,但是进入C-API的主要原因是它是在紧贴的上面,而不是Clang,直接从C++或者通过它的Python绑定来。Cling提供了对JIT的轻松访问,但您也可以直接从Clang(它的库,而不是AST)编写JIT。作为这样的EasyAccess的一个例子,在后端C-API中,您可以只将一个C++字符串转储到^ {< CD7> }函数(它与您的示例中的^ {CD8}}完全相同)。Cling folk计划直接从Cling为动态语言提供更好的界面,但这是一项正在进行的工作,还没有(AFAIK)可用

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

编辑:基本上,与其他工具不同,cppyy不提供起点列表(例如“所有类”),也不提供完整/真实的AST。您可以从后端复制cpp_cppyy.h头(它不是安装的一部分),只需包含它并使用它(所有符号都已导出),但您需要事先知道列表上课的时间。例如:

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))

但是正如您所看到的,它没有提供与原始代码的一对一结果。例如,所有编译器生成的构造函数和析构函数都列为方法

后端API中也没有像您链接的pygccxml文档中那样的东西。原因是,在cppyy的上下文中,这样做没有任何意义。例如,如果另一个标头加载了更多的run函数,该怎么办?如果它是一个模板化函数,并弹出更多的实例化,该怎么办?如果using namespace ...出现在后面的代码中,会引入更多的run重载,该怎么办

cppyy确实有GetAllCppNamesC-API函数,但不能保证它是详尽的。它的存在是为了在代码编辑器中完成制表符(在绑定作用域的定制__dir__函数中调用)。事实上,正是因为它不完整,cppyy-generator才使用libclang

您在评论中提到了Ginterpter,但这是我前面提到的历史的一部分:它是libclang提供的完整AST和Python所需的最低限度AST(如后端C-API)之间的一个命运多舛的中介。是的,您可以直接使用它(事实上,它仍然在后端C-API下使用),但它更笨重,没有什么好处

例如,要处理“获取所有‘运行’方法”示例,您可以执行以下操作:

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))

但是以前的接口(通过cppyylegation.TClass)可能无法保留,而Ginterpter接口真的很难看,正如您所看到的

我敢肯定,如果你想让cppyy取代pygccxml,你肯定不会高兴的。如果我是你的话,我会使用叮当作响的Python绑定

相关问题 更多 >