Cython:使用API​​嵌入Cython到C的分段错误

spa*_*ing 7 python cython

我试图在O'reilly Cython第8章之后将Cython代码嵌入到C中.我在Cython的文档中找到了这一段,但仍然不知道该怎么做:

如果想要使用这些函数的C代码是多个共享库或可执行文件的一部分,则需要在使用这些函数的每个共享库中调用import_modulename()函数.如果在调用其中一个api调用时遇到分段错误(linux上的SIGSEGV)崩溃,这可能表明包含生成分段错误的api调用的共享库之前没有调用import_modulename()函数崩溃的api电话.

我在OS X上运行Python 3.4,Cython 0.23和GCC 5.源代码是transcendentals.pyxmain.c:

main.c

#include "transcendentals_api.h"
#include <math.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  Py_SetPythonHome(L"/Users/spacegoing/anaconda");
  Py_Initialize();
  import_transcendentals();
  printf("pi**e: %f\n", pow(get_pi(), get_e()));

  Py_Finalize();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

transcendentals.pyx

cdef api double get_pi():
    return 3.1415926

cdef api double get_e():
    print("calling get_e()")
    return 2.718281828
Run Code Online (Sandbox Code Playgroud)

我正在使用setup.py和编译这些文件Makefile:

setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

setup(
    ext_modules=cythonize([
        Extension("transcendentals", ["transcendentals.pyx"])
    ])
)
Run Code Online (Sandbox Code Playgroud)

Makefile

python-config=/Users/spacegoing/anaconda/bin/python3-config
ldflags:=$(shell $(python-config) --ldflags)
cflags:=$(shell $(python-config) --cflags)

a.out: main.c transcendentals.so
    gcc-5 $(cflags) $(ldflags) transcendentals.c main.c

transcendentals.so: setup.py transcendentals.pyx
    python setup.py build_ext --inplace
    cython transcendentals.pyx


clean:
    rm -r a.out a.out.dSYM build transcendentals.[ch] transcendentals.so transcendentals_api.h
Run Code Online (Sandbox Code Playgroud)

但是,我来错了Segmentation fault: 11.有什么想法可以帮助吗?谢谢!

J.J*_*ala 1

在该Makefile中有

transcendentals.so: setup.py transcendentals.pyx
    python setup.py build_ext --inplace
Run Code Online (Sandbox Code Playgroud)

除非python引用/Users/spacegoing/anaconda/bin/python3它,否则应该替换它,因为该模块可能会编译为错误的 python 版本,因此无法加载。

main.c中,有一个调用import_transcendentals()不检查返回值,即导入是否失败或成功。如果发生故障,get_pi()指向get_e()无效的内存位置并尝试调用它们会导致分段错误。

此外,该模块必须位于可以找到它的地方。看来嵌入时,不会在当前目录中搜索python模块。可以更改环境变量以包含transcendentals.soPYTHONPATH所在的目录。

以下是将代码嵌入到 C 程序中的另一种方法,并避免导入问题,因为模块代码链接到可执行文件。

PyInit_transcendentals()本质上,缺少对 的调用。

当定义 cython 函数时,将生成文件transcendentals.hpublic,即

cdef public api double get_pi():
...
cdef public api double get_e():
Run Code Online (Sandbox Code Playgroud)

你的main.c应该有 include 指令

#include <Python.h>
#include "transcendentals.h"
Run Code Online (Sandbox Code Playgroud)

然后在main

Py_Initialize();
PyInit_transcendentals();
Run Code Online (Sandbox Code Playgroud)

应该没有#include "transcendentals_api.h"也没有import_transcendentals()

第一个原因是根据文档

但是,请注意,您应该在给定的 C 文件中包含 modulename.h 或 modulename_api.h,而不是同时包含两者,否则可能会出现冲突的双重定义。

第二个原因是,由于transcendentals.c链接到中的程序

gcc $(cflags) $(ldflags) transcendentals.c main.c
Run Code Online (Sandbox Code Playgroud)

没有理由导入先验模块。不过该模块必须初始化,PyInit_transcendentals()Python 3 也是如此