如何将一种语言(如Python)绑定到另一种语言(如C++)?

16 投票
6 回答
3438 浏览
提问于 2025-04-15 14:35

我对Python并不是特别精通,但我经常听到有人提到它和C/C++的连接。这个概念是怎么回事呢?Python(还有Java)是怎么和像OpenGL这样的C语言的API连接起来的呢?这些东西对我来说一直都是个谜。

6 个回答

4

一般来说,这些编程语言都有办法加载用C语言写的扩展。Java的接口叫做JNI(Java本地接口)。而Python则有详细的文档来介绍它的扩展接口。

对于Python来说,还有一个选择是ctypes模块,它可以让你在不需要编写自定义扩展代码的情况下,使用动态加载的C语言库。

7

这里主要讲的是一个叫做 FFI 的概念,也就是“外部函数接口”。在Java中叫JNI,在Python中叫“Python C API”,在Perl中叫XS,等等。了解这个通用术语对你深入研究这个话题很有帮助。

有了FFI,你可以直接写符合这个接口的C程序,或者使用代码生成器,从其他语言的代码中提取信息,生成这样的C代码(通常需要一些帮助,比如使用SWIG代码生成器时,你需要在C的头文件.h中添加一些特定的SWIG信息,以便生成更好的包装代码)。

还有一些特殊的语言,比如 Cython,它是Python的一个“扩展子集”,旨在方便生成FFI代码,同时保持大部分Python的语法和语义。对于主要使用Python的程序员来说,这通常是编写Python扩展模块的最简单方法,这些模块可以编译成快速的机器代码,并可能使用一些现有的C可调用库。

ctypes的方法与传统的FFI方法不同,尽管它自称是“Python的外部函数库”。它依赖于外部代码以DLL(或类似的动态库,比如Linux中的.so)的形式存在,并在运行时生成和执行代码,以访问这些动态加载的C代码(通常都是通过Python的显式编程来完成的——我还不知道有基于反射和ctypes代码生成的ctypes包装器)。对于简单的任务,比如用Python访问现有的DLL,这种方法很方便,因为不需要安装特别的东西,但我觉得它在规模上不如FFI的“链接器基础”方法(因为它需要更多的运行时操作等等)。我不知道除了Python的ctypes之外,还有没有其他语言采用这种方法(我想可能是有的,考虑到现在DLL和.so打包的普遍性,我也很想了解一下)。

13

用C89写的解释器和反射,谁知道呢?


我感觉你想了解的是机制,而不是API的链接或如何编码的说明。所以,按照我的理解……

主要的解释器通常是用C语言写的,并且是动态链接的。在动态链接的环境中,即使是C89也有一定的反射特性。特别是,dlopen(3)dlsym(3)这两个调用可以加载一个动态库(通常是ELF格式),并查找一个由字符串命名的符号的地址。得到这个地址后,解释器就可以调用一个函数。即使是静态链接,解释器也能知道那些名字已经编译进来的C函数的地址。

所以,实际上就是让被解释的代码告诉解释器去调用某个特定的本地函数,来自某个特定的本地库。

这个机制可以是模块化的。为解释器编写的扩展库,可以在脚本中调用dlopen(3)dlsym(3)的基本接口,并连接到一个解释器之前不知道的新库。

对于简单对象的值传递,通常会有几个原型函数来允许各种调用。但对于结构化数据对象(想象一下stat(2)),包装模块需要知道数据的布局。在某个时刻,无论是在打包扩展模块时还是安装时,一个C接口模块会包含适当的头文件,并结合手写代码构建一个接口对象。这就是为什么即使你已经在系统上有sqlite3,你仍然可能需要安装像libsqlite3-dev这样的东西;只有-dev包里有重新编译链接代码所需的.h文件。

我想我们可以总结一下:"这都是靠蛮力和无知来完成的"。:-)

撰写回答