【修改版本】Python调用C函数,cffi (附具体例子)

“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_dirsfoo.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']。这是因为我们已经把gslgslcblas里的必要信息打包进了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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值