如何为 C++ 库实现 Python 接口

How to Implement Python Interfaces for C++ Libraries

为 C++ 库创建 Python 接口的 best/standard 方法是什么?

我知道以前有人在这里问过这个问题,但那是在 2008 年,may/likely 从那以后情况发生了变化。

我环顾四周并测试了几种不同的方法,但无法确定哪种方法最好。到目前为止,我已经尝试过 Swig、ctypes 和 cppyy,并且认为 cppyy 是迄今为止要实现的 easiest/fastest。我看过有关 Swig 的建议,但花了很长时间才让 Swig 工作,而且结果并不令人印象深刻。有现行标准吗?为什么人们如此推荐 Swig 而我却没有听说过 cppyy?谢谢。

SWIG 自 1996 年 2 月以来一直存在,支持多种语言,而不仅仅是 Python。虽然现在的 cppyy 是从 2003 年 2 月开始的,作为 RootPython,它一直是嵌入 ROOT (http://root.cern.ch) 的,并且不能单独使用。自今年 3 月以来,所有三个主要平台的 PyPI 上的完整、简单的带轮子安装才存在,而 conda-forge(对于 Linux 和 Mac)两个月后才存在。所以,尽管它有很长的血统,但在更广泛的 Python 世界中,cppyy 确实很新鲜,这就是为什么我怀疑很多人还没有听说过它,而 SWIG 是它们所有人的(精神)祖先。

努力使 cppyy 可用的原因是它提供了很多其他绑定器没有的功能,并且不容易添加:兼容的 C++17 解析器([= Clang/LLVM 的 28=];自动模板实例化、交叉继承和回调,全部在 运行 时间(Cling 的 b/c);和更好的表现。它也不创建C扩展模块,所以你只需要为不同版本Python重新编译cppyy本身,但是none绑定代码。

现在,关于什么是最好的第一个问题。好吧,这取决于用例。例如,如果您需要的绑定不仅仅是 Python,SWIG 是您的最佳选择。如果你有很多模板,你不能在构建时全部实例化,需要性能和扩展,或者有一个有很多接口的 C++ 框架,那么 cppyy 很难被击败。如果您拥有现代 C++ 并且不希望任何 运行 时间依赖外部库,那么 PyBind11 就是它所在的位置。

这些天我不能推荐ctypes。唯一真正的好处是它是大多数 Python 的内置模块,但随着 PyPI 和 conda 的出现,它已经变得没有实际意义。如果你想要一个超轻量级的 C 绑定器(不是 C++,但你可以用 C 助手包装这些函数),那么请选择 CFFI。

关于您是否有标准的问题:没有,没有一种活页夹最适合所有用例。甚至比你提到的还要多很多,但他们中的许多人在相同的 space 中发挥作用(例如 SWIG 与 SIP,以及 PyBind11 与 boost.python),我不会推荐他们而不是您已经尝试过的。我确实想指出 AutoWIG,它是一个使用 Clang 和 PyBind11 或 boost.python 代码作为输出的生成器;和 cython,这是一个类似于 Python 的代码,用于编写 C 扩展模块,并且具有一些(有限的)C++ 支持。我一直觉得cython既不存在也不存在,但是很多人喜欢它,它在科学界和数学密集型代码中被广泛使用,因此它的质量得到了保证。

现在,即使没有 "standard",所有绑定器都可以将代理转换为 PyCapsule 对象并重新绑定它们。因此,尽管有时有点笨拙,但您实际上可以在一个应用程序中混合使用活页夹。

最后一点:CFFI 和 cppyy(通过 CFFI 的后端)在 PyPy 上具有接近本机的性能。不幸的是,cppyy 在 PyPy 上不像在 CPython 上那样是最新的(例如,仍然缺少交叉继承),但它已经到了。其他绑定器通过 Python C-API 工作,它在 PyPy 上具有完整的功能,但会阻止 JIT 完成其工作,从而降低性能。

完全免责声明:我是 cppyy 的作者,现在我只使用 cppyy、CFFI 和 PyBind11 来满足我的绑定需求。