python封装c接口_第12篇:Cython程序封装C函数接口

使用cythonize方式会自动生成gcc的链接和编译命令,本文涵盖如下知识点:Cython程序的手动编译的基本流程。

Cython程序如何调用外部C代码。

Cython文件的扩展名为.pyx和.pxd。 目前只需.pyx文件。 在后面的随笔中,会我将介绍.pxd的使用以及您可以使用的内容,为了清晰理解Cython生成这个*.so扩展模块的基本流程,本文会以计算一个三角形的面积作为例子。下图是 Cython的编译流程。Cython解析器将.pyx源文件生成c源码文件。

接下就是gcc编译->链接等常规步骤。

Cython编译基本流程

首先我们要准备两个文件,一个被cython程序导入的triangle.h头文件和要调用的triangle.c代码文件

triangle.h文件

在头文件里面,我们为cython程序声明了一个用于计算三角形面积的函数原型,名为你float calc_triangle_area (float,float,float)

如果对c/c++不太熟悉的话,请自行查找其他相关文章进一步阅读.这里不对c的任何语法细节解析.

#include #include #include #ifndef __TRIANGLE_H__#define __TRIANGLE_H__extern float calc_triangle_area(float a,float b,float c);

#endif

triangle.c文件

#include "triangle.h"float calc_triangle_area(float a,float b,float c){

if(a<0 || b<0 || c<0 || a+b<=c || b+c<=a || a+c<=b ){

printf("这个不是有效的三角形\n");

exit(-1);

}

float k=(a+b+c)/2.0;

return sqrt(k*(k-a)*(k-b)*(k-c));

}

然后创建一个名为cyTriangle.pyx的cython文件,在这个cython代码中,我们关心的是c代码,cdef关键子告诉cython解析器,被它声明的函数是要被解析为c函数,cdef extern from声明的头文件,表示要链接到函数原型指向的函数本体以及头文件中包含的其他函数原型。

#cython:language_level=3cdef extern from 'triangle.h':

cdef double calc_triangle_area(float,float,float)

def area(a,b,c):

return calc_triangle_area(a,b,c)

#end-def

之后,我们为之后的python代码如何调用该C代码指定了一个基本的python函数,cython维护者认为直接调用c代码存在风险,该python函数就是我们前面说过的类似胶水般的函数接口,其内部就是返回该c代码执行结果.

1.将pyx源码解析为c代码

$cython 3 cyTriangle.pyx

在执行的上面的代码的时候,没有提供3这个命令参数,意味着Cython解析器就无法按照python3.3+的语法去解析为C代码。如果出错这个提示,其实就是cython默认还基于python2的语法进行解析,这个年头果断切换为3的选项或在cython源文件头部加注这行注释 #cython:language_level=3,告知cython解析器我们使用的是python3的解析器.

没有意外的话,现在所在目录应该多了一个cyTriangle.c的文件

2将c文件编译成目标文件(*.o文件)

gcc -g -O2 -fpic `python3-config --cflags` -c cyTriangle.c -o cyTriangle.o

gcc -g -O2 -fpic -c triangle.c -o triangle.o

无意外的话,当前目录已经生成了两个目标文件

3. 将多个目标文件连接成动态链接库文件(*.so文件)

如果你安装的python是通过brew install python3这样的命令安装的话,建议在这行上面的命令的时候,先LIBS=$(python3-config --ldflags),缓存到一个临时变量当中,然后才执行以下指令

gcc -g -O2 -shared ${LIBS} -o triangle.so triangle.o cyTriangle.o

我写本文的时候macOS 10.14.6,我想如果读者执行上面的链接指令没有添加LIBS临时变量所指向的libpython3.7.so的路径,应该会出现如下图错误,因为macOS内置的还是python2.7,gcc会提示无法找到libPython3.7.so这个共享对象.

最后不出意外的话,当前目录应该生成了一个名为triangle.so的文件

ok,我们进入python环境import 一下我们刚才的编译后的共享对象

如果你在实际项目过程中引用其他人写C/C++代码(不是我这里的简单示例),整个的编译的过程没有出现错误,但是在python环境中加载编译后的扩展就提示import error Symbol not found此类错误的话,我猜你是否很可能踩中这些坑,可以参考一下两点进行排查.多半是c/c++头文件是否加载的head文件是否完整.

请看扩展阅读的部分

扩展阅读:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值