将C数据结构和C API导出到Python
我有一个C语言的库,里面有C语言的接口。我想把这些函数和C语言的数据结构暴露给Python,这样我就可以在我的Python脚本中使用这个库。
这个库里有200多个函数和大约50个数据结构,所以如果能自动生成C和Python之间的“连接”代码,那就太好了。
我现在在研究SWIG,因为它看起来是个不错的选择。还有其他我应该考虑的替代方案吗?(如果有的话,为什么呢?)
最后,如果SWIG确实是个好选择,有人能给我推荐一个好的教程,教我如何从现有的C库写一个Python扩展模块吗?
2 个回答
我使用SWIG 2.x把一个比较复杂的C和C++接口转换成其他语言,这让我节省了很多时间。
SWIG有一个重要的限制,就是它对结构体里面的结构体处理得不是很好。在C++中,类也是如此。我对C/C++的API有完全的控制权,所以在开始之前,我把那些使用了内部结构体的结构体进行了扁平化,避免了这个问题。
我首先建议你为SWIG定义一个基本模块,只需导入你的头文件,然后看看SWIG的反应。就像编译器一样,SWIG会输出它发现的警告和错误,这些就是你需要解决的问题。
当我所有的函数都能正常工作后,我对结果并不是完全满意。SWIG会逐字翻译你的所有函数,但问题是,有些在C/C++中很常见的写法在Python中看起来就很奇怪。例如,指定输出参数为指针的函数在Python中就不太符合习惯。比如,考虑这个在C/C++中很常见的函数:
unsigned int getPath(char* buffer, unsigned int size);
这个函数接收一个缓冲区和一个大小,然后用一个字符串填充这个缓冲区,返回实际使用了多少字节,如果出错则返回0。在Python中,传递一个缓冲区(字符串)和一个大小作为参数会显得非常奇怪,所以对于这种风格的函数,我添加了替代版本,使其更友好。在这种情况下,我做了这样的处理:
const char* getPath();
这个函数的实现简单地将一个最大大小的静态缓冲区传递给原始的getPath()
,并返回静态缓冲区的地址,如果出错则返回NULL。需要说明的是:我使用的是C++编译器,所以我可以定义两个参数不同的getPath()
函数。在C语言中,你需要为每个函数使用不同的名称。SWIG会自动将char*转换为Python字符串,所以这个转换非常顺利。
除了在C/C++中定义替代函数外,你还可以给SWIG提供指示,告诉它你希望如何转换某些函数及其参数。例如,SWIG允许你将一对缓冲区和大小参数绑定在一起,把它们当作一个单一的字符串参数来处理。还有很多这样的便利转换。
至于教程,我没有找到什么有用的资料,除了官方的SWIG文档,那个文档相当不错,还有专门针对Python的部分。
希望这些对你有帮助。