python调用C++函数
之前写毕业设计,有一个地方我觉得实现得不太合理,因为有一个地方涉及用C++对输入的图像进行推理,而我没有找到合适的C++库来加载图像,与项目的甲方问起,他们的意见是先用python做预处理,然后将张量写到文件中去,再由C++代码读取张量,进行推理后,再将推理后得到的张量写到文件中去,最后再用python做后处理。这样虽然简单,但是会带来大量的IO开销,我觉得很不合理。虽然项目交付了,但是写毕业论文的时候我还是觉得这个地方值得优化,就花了点时间折腾了一下。
python提供了ctypes
来调用动态库中的函数,它提供了与C 兼容的数据类型,可使用该模块以纯Python 形式对这些库进行封装,因此python也被称为胶水语言。
无参数、返回值的C++函数调用
在这里写了一个简单的hello world函数,给python调用:
1 | // print.cpp |
注意,在C++的函数如果要被ctypes
调用需要使用extern "C"
,因为ctypes
是基于C语言编写的,需要让C++函数的符号变成C符号,不然在python中调用的时候,无法找到对应的函数符号。
然后把上面的函数编译成so
动态库:
1 | g++ -o libprint.so -shared -fPIC print.cpp |
接下来就编写python程序调用print函数:
1 | // print.py |
执行:
1 | $ python3 print.py |
这是最简单的格式,比较麻烦的是参数,特别是数组参数的处理。
有参数、返回值的C++函数调用
- 简单类型参数及返回值
在这里编写了一个简单的sum函数为例:
1 | // sum.cpp |
编译成动态库:
1 | g++ -o libsum.so -shared -fPIC sum.cpp |
然后用python调用:
1 | from ctypes import * |
在这里因为参数和返回值都是基本类型int,所以较为简单。
- 复杂参数或返回值
我的需求涉及到numpy数组的处理,而如果直接把numpy数组传递给函数,会出现类型不匹配的错误。阅读了文档后,我觉得可以把numpy转化为bytes数组的形式,然后以buffer的形式传递给C++函数,C++再做一次强制类型转换就可以得到数组了。注意需要匹配好数据类型:
C++代码如下:
1 | // mul_array.cpp |
python代码如下:
1 | from ast import Num |
在处理返回值的时候,需要指定返回值类型为c_void_p
,即void*
。在C++程序执行完毕后,返回值ret为内存中的一段地址,然后用string_at
来获取这段地址,再用numpy的frombuffer函数从bytes数组中获取numpy数组。在这个过程中,需要保证numpy数组的类型和C++强制转换的类型一致,不然计算出来的结果就会由于类型格式的不同出现错误。
把mul_array.cpp
编译成动态库后,执行python:
1 | $ python3 mul_array.py |
可以看到成功将数组传入,并成功返回了数组。
似乎也可以用其他更简单的方法来实现我的需求,不过因为我的需求已经完成,有空再来折腾。