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

La *_*Fon 6 c++ python swig ctypes cppyy

为 C++ 库创建 Python 接口的最佳/标准方法是什么?

我知道这个问题以前曾在这里问过,但那是在 2008 年,从那时起事情可能/可能已经发生了变化。

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

Wim*_*sen 12

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 解析器(b/c of​​ Clang/LLVM ); 自动模板实例化、交叉继承和回调,全部在运行时(Cling 的 b/c);和更好的性能。它也不会创建 C 扩展模块,因此您只需要为不同版本的 Python 重新编译 cppyy 本身,而不需要任何绑定代码。

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

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

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

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

最后一点:CFFI 和 cppyy(通过 CFFI 的后端)在 PyPy 上具有近乎原生的性能。不幸的是,cppyy 在 PyPy 上并不像在 CPython 上那样最新(例如交叉继承仍然缺失),但它正在到达那里。其他绑定器通过 Python C-API 工作,该 API 在 PyPy 上具有完整功能,但阻止 JIT 执行其工作,从而导致性能降低。

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