大家好!各位伙伴在接触Python代码时,在某些情况下,需要调用C++的一些函数。自己最近几天在弄这个问题,中间遇到了到了不少的问题,并且网上相关的内容比较少,因此分享这篇文章。本篇文章对很多底层原理没有过多解释,主要是为了去简单地使用,解决一些问题。中间一些内容可能不大准确,但是希望也能帮助大家,如果大家觉得有帮助的话,点个赞哦
本篇文章会分享Python代码如何调用c++,如何传递数值型参数,字符串参数,指针,以及最实用的Python传递list给c++中的数组
自己查阅了很多博客,这方面的资料比较少,然后主要从B站的一个视频中得到了不少灵感,非常感谢那位老师,如果大家有时间可以去学习一下,会有很多收获。主要参考的资源如下:
Ctypes官方文档
B站一位大哥的视频
在调用C++时,主要使用的是dll文件以及.so文件,很多博客有这方面的介绍,大家可以自行进行查阅。自己主要是使用.so文件来进行调用,在使用时应该差别不大。
首先来看一个简单的示例,大致了解下基本流程,后面会介绍一些更加复杂的情况,比如传递简单的参数,如数字,字符串等,更复杂的还有Python 传递列表 给C++,以及如何从c++ 返回数组 给Python,自己暂时涉猎的深度就这些,如果有大家想要的东西,就接着往下看吧。
咱们来写个简单的HelloWorld程序,.py文件和.cpp文件在一个目录下:
#include
#include
using namespace std;
extern "C"
void test1(){
cout << "Hello world" << endl;
}
int main(){
return 0;
}
这里要注意如果是c++的话,要想被Python调用的函数要加extern “C”,暂时记住即可,不知道原因暂时不影响。
然后生成.so文件,这里首先要安装一下g++,具体教程很多,大家可以自行搜索去进行解决,然后使用如下命令生成.so文件
g++ -fPIC -shared HelloWorld.cpp -o HelloWorld.so
执行完此命令后,可以发现多了一个.so文件
现在我们要开始编写我们的Python代码来调用我们写的c++函数了,Python代码如下:
import ctypes
dll = ctypes.cdll.LoadLibrary
lib = dll('./HelloWorld.so')
lib.test1()
前面相当于一个固定的写法,当然也有别的写法,在下才疏学浅,这一种能用就一直用的是这个啦,运行此文件,发现能正确输出:
当然,各位的需求远没有这么简单,有时我们需要传递一些参数,这里介绍一下自己了解的几种参数,包括整数,浮点数,字符串。此过程涉及Python,ctypes,c++之间的数据类型的转化,首先上一张官方文档的图:
int类型可以直接传递,如下所示:
extern "C"
void testInt(int a){
printf("调用了int测试函数\n");
printf("接收的值为 %d", a);
}
执行如下代码
lib.testInt(1)
# lib.testInt(ctypes.c_int(1)) # 进行转换也可以正确输出
再测试浮点型,浮点型数据需要进行转换
extern "C"
void testFloat(float a){
printf("调用了flost测试函数\n");
printf("接收的值为 %f", a);
}
若直接执行
lib.testFloat(66.6666)
lib.testFloat(ctypes.c_float(66.6666))
若传递普通的字符,需要进行简单的转换,直接传递会出现问题
str = 'Give a thumb up!'
lib.testString(str, len(str))
此时进行简单的转化即可,在字符串前加个b,声明为字节对象即可。另外还有宽字符类型,进行相应的转化即可。
str = b'Give a thumb up!'
lib.testString(str, len(str))
前面分享了常见参数的传递,有时需要从c++函数返回的结果。默认返回的类型是int类型,若返回的是其他的类型,需要通过restype来进行指定
extern "C"
int testResInt(){
return 1;
}
extern "C"
float testResFloat(){
return 66.6666;
}
extern "C"
char * testResString(){
return "Give a thumb up";
}
Python代码如下所示:
print(lib.testResInt())
lib.testResFloat.restype = ctypes.c_float
print(lib.testResFloat())
lib.testResString.restype = ctypes.c_char_p
print(lib.testResString())
若想Python传递指针给c++,此时通过argtypes可以指定传入的参数类型
extern "C"
int * testIntPointer(int * p){
*p = 1;
return p;
}
Python代码如下:
lib.testIntPointer.argtypes = (ctypes.POINTER(ctypes.c_int), )
lib.testIntPointer.restype = ctypes.POINTER(ctypes.c_int)
I = ctypes.c_int(100)
re = lib.testIntPointer(ctypes.byref(I))
print("返回值的类型", type(re))
print("返回值为", re.contents)
Pyhton中什么数据类型使用地最多,没错,当然就是list了。那么现在就出现一个复杂的问题,也是当时自己最需要解决的一个问题,就是如何将列表类型的数据传递给c++函数。c++中跟list数据类型相似的就是数组了,因此现在的问题就是如何将列表传递给c++的数组。
extern "C"
void testArray(int *arr, int size){
printf("调用了testArray函数\n");
for(int i = 0; i < size; ++i)
cout << arr[i] << " ";
}
a = [i for i in range(10)] # 生成一个列表
TenArrType = ctypes.c_int * len(a)
ca = TenArrType(*a)
lib.testArray.argtypes = (TenArrType, )
lib.testArray(ca, len(a))
所以传递list的步骤为,首先c++代码正常编写即可,在Python代码中,首先要声明个类型 (list中数据对应的ctypes的类型) * (list的长度),例如示例中的 ctypes.c_int * len(a),然后再利用 (类型)(*列表) 构造一个新的对象,个人感觉可以理解成就是将list中的数据拷贝到了这块新的空间。最后再指定一下传入的参数类型,后面就可以直接调用了
这块涉及的问题比较复杂,如果要真正实现此功能,好像还挺麻烦的,我暂时还没学会。在此我分享一个简单粗暴的方法,能解决我们的需求。就是上面我们已经学习了如何去传递列表给c++,在c++中这是数组的形式,c++代码中在这块内存可以进行访问并写入,因此我们只需要在Python中事先定义好这样的变量,在c++中直接将值写入其中就行,这样便有异曲同工之妙。
extern "C"
void testArray2(int *arr, int * arr2, int size){
for(int i = 0; i < size; ++i)
arr2[i] = arr[i] * 4;
}
a = [i for i in range(10)] # 生成一个列表
b = [0] * len(a)
TenArrType = ctypes.c_int * len(a)
ca = TenArrType(*a)
cb = TenArrType(*b)
lib.testArray2.argtypes = (TenArrType, TenArrType, )
lib.testArray2(ca, cb, len(a))
for i in range(len(b)):
print(cb[i], end=' ')
发现输出如下
到此,这次的分享就结束了,自己弄的也不深入,只是为了解决这个问题,有些地方不对望指正,如果觉得有帮助点个赞哦