用Cython封装C代码的简单示例- 传递int和结构体

10 投票
1 回答
3882 浏览
提问于 2025-04-18 12:26

下面的代码可以运行,但我不太确定为什么

我使用的是:

  • 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 文件里。

撰写回答