numpy ctypeslib 与 ctypes接口使用说明

作者:elfin


使用numpy.ctypelib或者ctypes库可以实现python直接待用C++。numpy.ctypeslib后端是基于ctypes实现的!所以接口是类似的。

如果只想看如何调用外部接口可以直接查看 1.4.3.1 动态链接库使用成功案例,其他部分有很多试错的过程!

Top --- Bottom


一、numpy.ctypeslib使用说明

numpy是python做计算非常基础的一个库,安装就直接使用pip install numpy即可。

导入python包import numpy.ctypeslib as ctl

1.1 准备好一个C++计算文件

#include <iostream>
using namespace std; int Paritition1(int A[], int low, int high) {
int pivot = A[low];
while (low < high) {
while (low < high && A[high] >= pivot) {
--high;
}
A[low] = A[high];
while (low < high && A[low] <= pivot) {
++low;
}
A[high] = A[low];
}
A[low] = pivot;
return low;
} void QuickSort(int A[], int low, int high) //快排母函数
{
if (low < high) {
int pivot = Paritition1(A, low, high);
QuickSort(A, low, pivot - 1);
QuickSort(A, pivot + 1, high);
}
} int main()
{
int arr[5] = { 9, 2, 8, -6, 5 };
QuickSort(arr, 0, 4);
for (int i = 0; i < 4; i++)
{
cout << arr[i] << " ";
}
cout << arr[4] << endl;
}

上面是一段C++版的快排代码。假设我们已经有这个cpp文件,那么如何在python中使用?

需要独立编译时,Windows系统可以在头文件中使用下面的语句

// __declspec(dllexport)  导出到库中(win系统环境下.dll文件链接宏)
extern "C" __declspec(dllexport) void QuickSort(int A[], int low, int high);

在Linux中需要使用extern进行声明

1.2 ctypeslib主要的五个接口

ctypeslib给我们提供了五个接口使用:

  • load_library加载c++文件;
  • ndpointer
  • c_intp
  • as_ctypes numpy数据类型转为ctypes类型
  • as_arrayc++数据转为numpy数据类型

Top --- Bottom

1.3 加载编译后的文件

我这里是在Windows上面测试,生成exe文件后可以直接读入。

c_quick = ctl.load_library(
libname="quick.exe",
loader_path=r"H:\quick\x64\Debug"
)

查看c_quick类型可以发现它是一个<class 'ctypes.CDLL'>类型。类型是没错的,但是_FuncPtr函数接收到QuickSort字符串时,却显示找不到这个函数;如果我们直接使用cpp,或者dll等文件,有显示: 不是有效的win32应用程序。

因为我们的执行环境是64位的,而visual studio是基于32位的,理论上有两种办法解决:

  • 将python程序改成32位版本,当然这种方法对我们来说一般难以接受;
  • 将外部链接编译为64位的,我在studio里面设置了x64,结果设置了一个寂寞。

为了不浪费太多时间,我将这部分搬到Ubuntu系统上进行测试。

1.4 Linux系统下加载编译后的文件

这里我们记录从文件生成到最终调用的全过程。

1.4.1 书写文档

$ sudo vim quik.c
# 将1.1的C++文件内容书写进文档并保持

1.4.2 编译、打包源文件

编译链接为可执行文件

$ sudo g++ -Wall quik.c -o quick
$ ./quick
-6 2 5 8 9

这里对main函数的默认数组进行了排序!

编译为目标文件

$ sudo g++ -Wall -c quik.c -o quick.o
$ ls
quick quick.o quik.c

生成静态链接库

$ sudo ar cr libquick.o quick.o
$ ls
libquick.o quick quick.o quik.c

生成动态链接库

$ sudo g++ -Wall -shared -fPIC quik.c -o libquick.so
$ ls
libquick.o libquick.so quick quick.o quik.c

Top --- Bottom

1.4.3 python加载外部链接库

我们尝试从不同的文件进行加载,分别是c++源文件、可执行文件、.o目标文件、外部静态链接库、外部动态链接库。

首先说明结果:c++源文件、可执行文件都不能被加载。

加载c++源文件报错:OSError: quik.c: invalid ELF header

加载可执行文件quick报错:OSError: no file with expected extension

加载目标文件报错:OSError: quick.o: only ET_DYN and ET_EXEC can be loaded

加载外部静态链接库报错:OSError: libquick.o: invalid ELF header

加载外部动态链接库报错:undefined symbol:“QuickSort”

现在我们只能使用main函数,其他函数调用会报未定义符号的错误,下面我们先基于so动态链接库走通这个流程。

1.4.3.1 动态链接库使用成功案例

首先我们模拟正常的项目,进行独立编译。我们将quik文件拆分为quik.cmain.c

问题的解决方案可以参考linux动态库so调用外部so,运行时出现undefined symbol

我们生成如下的quik.cmain.c文档:

#include <iostream>
using namespace std; //extern "C" C++中编译c格式的函数,如果利用c语言编译不需要
extern "C" void QuickSort(int A[], int low, int high); int Paritition1(int A[], int low, int high) {
int pivot = A[low];
while (low < high) {
while (low < high && A[high] >= pivot) {
--high;
}
A[low] = A[high];
while (low < high && A[low] <= pivot) {
++low;
}
A[high] = A[low];
}
A[low] = pivot;
return low;
} void QuickSort(int A[], int low, int high) //快排母函数
{
if (low < high) {
int pivot = Paritition1(A, low, high);
QuickSort(A, low, pivot - 1);
QuickSort(A, pivot + 1, high);
}
}
#include <iostream>
using namespace std; // 声明可以写 在 头文件中
extern "C" void QuickSort(int A[], int low, int high); int main(){
int arr[5] = { 9, 2, 8, -6, 5 };
QuickSort(arr, 0, 4);
for (int i = 0; i < 4; i++){
cout << arr[i] << " ";
}
cout << arr[4] << endl;
}

上面我们使用extern进行了QuickSort函数的声明,这样就可以不使用quik.h头文件,要注意的是声明中使用的是“C”进行编译防止函数名被优化!基于此项改动就可以得到如下效果

python调用so动态链接库

$ sudo g++ -Wall -c main.c
$ sudo g++ -Wall -c quik.c -o quick.o
$ sudo g++ -Wall -shared -fPIC main.c quik.c -o libquick.so
$ python
>>> import numpy.ctypeslib as ctl
>>> ctl_lib = ctl.load_library("libquick.so", "./")
>>> import numpy as np
>>> arr_1d = ctl.ndpointer(
... dtype=np.int32,
... ndim=1,
... flags="CONTIGUOUS"
... )
>>> ctl_lib.QuickSort.restypes = None
>>> from ctypes import c_int, c_float
>>> ctl_lib.QuickSort.argtypes = [arr_1d, c_int, c_int]
>>> arr1 = np.array([5, -6, 8, 4, 7, 6, 3], dtype=np.int32)
>>> ctl_lib.QuickSort(arr1, 0, len(arr1))
>>> print(arr1)
[-6 3 4 5 6 7 8]
>>> type(arr1)
<class 'numpy.ndarray'>

注: 关于最好还是写在声明头文件中,main.c文件引入头文件;但是函数定义文件中的声明是一定要有的!

动态库可以,静态库却不可以?

直接加载quik.c源文件生成的静态链接库确实是不行!即使我们已经声明还是会有invalid ELF header错误,也不难理解,声明是解决不了这个问题的。鉴于我们常使用动态链接库,静态库该如何调用留作悬念吧。不过根据这个报错,很可能我们调用不起来。

1.4.3.2 python调用外部链接库总结

  • 调用外部C/C++接口,我们最好使用动态链接库;

  • 动态链接库生成的时候一定要注意进行声明extern "C" void QuickSort(int A[], int low, int high);

    不然加载后会找不到函数名,这个名字会被优化成其他

  • 更多细节可以参考官方文档https://scipy-cookbook.readthedocs.io/items/Ctypes.html


Top --- Bottom

完!

最新文章

  1. jQuery之回调对象
  2. TCP/IP中链路层的附加数据(Trailer数据)和作用
  3. Jenkins离线下载插件,并安装
  4. ThinkPHP 知识点链接
  5. paip.获取地理位置根据Ip
  6. HDU 1695
  7. C# 堆和栈的区别-该文转自:http://www.itcodes.cn/746.html | 程序人生
  8. 通过WebBrowser取得AJAX后的网页
  9. SpringMVC深入探究(1)——DispatcherServlet与初始化主线
  10. LightOJ 1038-Race to 1 Again(概率dp)
  11. YCbCr
  12. synaptics驱动,插入USB鼠标禁用触控板注册表
  13. WindowsAPI 之 CreatePipe、CreateProcess
  14. Django用普通user对象登录的必须准备步骤
  15. 导入项目 idea
  16. 从零开始搭建一个规范的vue-cli 3.0项目
  17. 自定义Section
  18. Linux编程 22 shell编程(输出和输入重定向,管道,数学运算命令,退出脚本状态码)
  19. LSTMs 长短期记忆网络系列
  20. python 中文件输入输出及os模块对文件系统的操作

热门文章

  1. @RestController注解的作用
  2. 3组-Alpha冲刺-1/6
  3. 大爽Python入门教程 2-5 *拓展实践,对比与思考
  4. Python科普系列——类与方法(下篇)
  5. DP+单调队列详解+题目
  6. SpringCloud升级之路2020.0.x版-45. 实现公共日志记录
  7. [POI2002][HAOI2007]反素数
  8. 使用bioawk对基因组fasta序列ID(染色体/scaffold名称)排序?
  9. MEGA软件——系统发育树构建方法(图文讲解) 转载
  10. HMS Core Discovery直播预告 | AI画质增强 ,开启超清视界