“C Foreign Function Interface for Python”(cffi)
假设存在一个C语言函数,定义在foo.c
文件中,这个函数调用了GSL(GNU Scientific Library)里面的Bessel函数。
// foo.c
#include <gsl/gsl_sf_bessel.h>
double foo(double a, double b){
return gsl_sf_bessel_J0(a + b);
}
如果python要用cffi模块调用函数foo
。首先,我们要使用cffi中的cdef函数定义输入和输出的数据类型 (类比头文件)。
# demo_1.py
from cffi import FFI
ffi = FFI()
ffi.cdef("double foo(double, double);")
下一步,我们要使用cffi中的verify函数,把foo
在C语言中的具体实现传给python。
有两种方式可以实现:
1. 把源文件 foo.c
直接传递python
2. 把foo.c
编译成shared library,然后再传递给python
针对第一种方式:
# demo_1.py (cont.)
import os
file_dir = os.path.abspath('.')
lib = ffi.verify("#include <foo.c>", include_dirs=[file_dir], libraries=['gsl', 'gslcblas'])
verify
函数有三个参数,分别是C语言源文件名,路径,库文件;
foo.c
包含函数foo
定义的C语言源文件;include_dirs
是
foo.c
所在的位置,因为demo_1.py
文件和foo.c
放在了同一个文件家里,所以可以用os.path.abspath('.')
来提取foo.c
的文件夹地址;参数libraries
是需要用到的库文件,gsl库文件和gslclbas文件。因为函数foo
调用了GSL库里面的Bessel函数。 接下来,可以在python中直接调用C语言定义的函数foo
了:
# demo_1.py (cont.)
a, b = 1.2, 3.4
print lib.foo(a, b)
针对第二种方式:
通过shared library来传递函数foo
的定义,这么做的好处是把foo
和其他用到的库文件比如GSL“打包”成一个新的库文件,比如叫libfoo.so
,之后就可以在python中直接使用libfoo.so
而无需再指定其他库文件了。具体方法如下:
gcc foo.c -shared -lgsl -lgslcblas -o libfoo.so
注意在这一步中我们把gsl和gslclbas这两个库文件“打包”进了libfoo.so
,然后我们就可以在python中通过cffi来调用libfoo.so
了。
# demo_2.py
from cffi import FFI
ffi = FFI()
ffi.cdef("double foo(double, double);")
import os
file_dir = os.path.abspath('.')
lib = ffi.verify("#include <foo.c>", include_dirs=[dir], library_dirs=[dir], libraries=['foo'])
a, b = 1.2, 3.4
print lib.foo(a, b)
注意观察verify
中的两点使用变化:
1. 多了一个参数library_dirs
用来说明库文件foo
的位置,这是因为libfoo.so
经常不在系统默认的PATH
上(而gsl
等库文件确实一般安装在PATH
上,而且我们也需要用libraray_dirs
这个参数来指明)。
2. 参数libraries
的值变成了[foo]
,而不是['gsl', 'gslcblas']
。这是因为我们已经把gsl
和gslcblas
里的必要信息打包进了libfoo.so
里面。
最后,假如函数foo
的定义非常复杂,我们不想把它直接传给python,那么就可以定义一个头文件foo.h
来包含foo
的函数声名,
// foo.h
#ifndef foo_h__
#define foo_h__
double foo(double, double);
#endif
然后把该头文件传递过去就可以了,即
ffi.verify("#include <foo.h>", include_dirs=[dir], library_dirs=[dir], libraries=['foo'])
假设有两个文件: vect3.h 和 vect3.c.
vect3.h如下:
#define HELLO 3
typedef struct
{
double x;
double y;
double z;
char* label;
}Vec3;
Vec3* getVec3(double x, double y, double z, char* label);
void printVec3(Vec3* v);
void addScalar(Vec3* v, double s);
void subScalar(Vec3* v, double s);
void mulScalar(Vec3* v, double s);
void divScalar(Vec3* v, double s);
void extendVec3(Vec3* v, char c, double s);
void deleteVec3(Vec3* v);
vect3.c定义为:
#include <stdio.h>
#include <malloc.h>
#include "vect3.h"
Vec3* getVec3(double x, double y, double z, char* label){
Vec3* v = (Vec3*)malloc(sizeof(Vec3));
v->x = x;
v->y = y;
v->z = z;
v->label = label;
return v;
}
void deleteVec3(Vec3* v){
if (v==NULL) return;
free(v);
v = NULL;
}
void printVec3(Vec3* v){
if(v==NULL)return;
printf("x = %.4f, y = %.4f, z = %.4f, label=%s\n", v->x,v->y,v->z,v->label);
}
void addScalar(Vec3* v, double s){
if (v==NULL) return;
v->x += s;
v->y += s;
v->z += s;
}
void subScalar(Vec3* v, double s){
if(v==NULL) return;
addScalar(v, -s);
}
void mulScalar(Vec3* v, double s){
if(v==NULL) return;
v->x *= s;
v->y *= s;
v->z *= s;
}
void divScalar(Vec3* v, double s){
if (v==NULL||!s) return;
v->x /= s;
v->y /= s;
v->z /= s;
}
void extendVec3(Vec3* v, char c, double s){
if (v == NULL ) return;
switch (c){
case '+':
addScalar(v, s);
break;
case '-':
subScalar(v ,s);
break;
case '*':
mulScalar(v, s);
break;
case '/':
divScalar(v, s);
break;
}
}
int main(int argc, char const *argv[])
{
double x, y, z;
x = 1.0;
y = 2.0;
z = 3.0;
char* label = "ssdf";
Vec3* v = getVec3(x,y,z,label);
printVec3(v);
addScalar(v, 3.7);
printVec3(v);
deleteVec3(v);
return 0;
}
我们对这个文件进行编译,编译为libvect3.so。这样我们在cffi中就能调用这个动态链接库。
现在我们引入cffi中的FFI类。
from cffi import FFI
ffi = FFI()
此时我们要先定义好这个动态链接库都有什么函数什么常量可以调用。
即,在cffi中使用ffi.cdef这个函数。
ffi.cdef("""
#define HELLO 3
typedef struct
{
double x;
double y;
double z;
char* label;
}Vec3;
Vec3* getVec3(double x, double y, double z, char* label);
void printVec3(Vec3* v);
void addScalar(Vec3* v, double s);
void subScalar(Vec3* v, double s);
void mulScalar(Vec3* v, double s);
void divScalar(Vec3* v, double s);
void extendVec3(Vec3* v, char c, double s);
void deleteVec3(Vec3* v);
""")
这其实就是我们的头文件。
现在可以加载我们的动态链接库。
lib = ffi.dlopen('./libvect3.so')
这时,我们就可以调用C函数了。
原文地址:http://blog.csdn.net/win_in_action/article/details/46564963
http://www.debugrun.com/a/fjyxcbl.html