用Cython封装C代码的简单示例- 传递int和结构体
下面的代码可以运行,但我不太确定为什么。
我使用的是:
- Mac OSX 10.8.5
- 通过brew安装的Python 2.7.5
- Cython 0.20.2
这段代码主要来自于这个视频教程和这个git页面,但不幸的是,刚开始并不能直接使用。
这个代码的目的是通过Python访问一个简单的C函数,这个函数可以接受整数或整数结构,并将它们相加。
这个过程需要5个文件,具体如下:
adder.c
:包含两个加法函数的C代码:add(标量输入)和pair_add(结构输入)adder.h
:为adder.c提供的头文件c_adder.pxd
:一个Cython头文件,基本上告诉Cython要关注主头文件中的哪些部分cy_adder.pyx
:用于在Python命名空间中定义这两个函数的Cython代码setup.py
:一个distutils文件,用于编译Cython代码
这个过程会生成两个文件:
c_adder.c
:一个中间的Cython C文件c_adder.so
:可以导入到命名空间中的Python模块
输入文件如下:
adder.c
#include <stdlib.h>
#include "adder.h"
int
pair_add(PAIR * ppair) {
return ppair->x + ppair->y;
}
int
add(int x, int y) {
return x + y;
}
adder.h
typedef struct {
int x;
int y;
} PAIR;
int pair_add(PAIR * ppair);
int add(int, int);
c_adder.pxd
cdef extern from "adder.h":
ctypedef struct PAIR:
int x
int y
int add(int x, int y)
int pair_add(PAIR * ppair)
cy_adder.pyx
cimport c_adder
def add(x, y):
return c_adder.add(x, y)
def pair_add(x, y):
cdef c_adder.PAIR pair
pair.x = x
pair.y = y
return c_adder.pair_add(&pair)
setup.py
from setuptools import setup
from setuptools.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize
ext_modules = cythonize([Extension("cy_adder", ["cy_adder.pyx",'adder.c'])])
setup(
name = 'Hello world app',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)
我通过运行
$ python setup.py build_ext --inplace
在上述文件所在的同一目录下成功创建了一个.so文件。然后可以将cy_adder
加载到Python解释器的命名空间中。
问题
在setup声明中,我将adder.c
作为辅助模块包含在内。
如果我不这样做,在导入.so
文件时会出现以下错误:
ImportError: dlopen(./cy_adder.so, 2): Symbol not found: _add
Referenced from: ./cy_adder.so
Expected in: flat namespace
in ./cy_adder.so
我是不是漏掉了什么步骤,以至于不需要在setup命令中明确传递adder.c
?这样做会不会让我面临一些潜在的不稳定性?
1 个回答
2
要让 adder.c
文件知道在哪里找到 add
的实现,你需要在某个地方引用它。正如你发现的,把 adder.c
列为一个额外的源文件是可以的。不过,更常见的做法是把它编译成一个叫做 libadder.so 的共享库,然后通过在你的扩展声明中使用 libraries
参数来链接它。
对于这么简单的事情,你也可以直接写
cdef extern from "adder.c": # note the .c
int add(int x, int y)
...
或者把整个实现放在 .h 文件里。