2016-06-25 48 views
4

我試圖嵌入用Cython代碼爲C以下O'reilly Cython book第8章我發現用Cython的documentation這一段,但還是不知道該怎麼辦:用Cython:分段故障使用API​​嵌入用Cython到C

If the C code wanting to use these functions is part of more than one shared library or executable, then import_modulename() function needs to be called in each of the shared libraries which use these functions. If you crash with a segmentation fault (SIGSEGV on linux) when calling into one of these api calls, this is likely an indication that the shared library which contains the api call which is generating the segmentation fault does not call the import_modulename() function before the api call which crashes.

我在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; 
} 

transcendentals.pyx

cdef api double get_pi(): 
    return 3.1415926 

cdef api double get_e(): 
    print("calling get_e()") 
    return 2.718281828 

我使用setup.pyMakefile編譯這些文件:

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"]) 
    ]) 
) 

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 

Howev呃,我來到錯誤Segmentation fault: 11。任何想法可以幫助這個?謝謝!

+0

如果你嘗試從python解釋器中導入超驗者,會發生什麼?在我的電腦上,導入錯誤:/tmp/q/transcendentals.so:undefined symbol:PyUnicodeUCS4_DecodeUTF8'。 'import_transcendentals()'在成功時返回'0',在失敗時返回'-1',即應該檢查返回值。 –

回答

1

在這種Makefile中有

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

除非python/Users/spacegoing/anaconda/bin/python3,應及時更換,因爲模塊可以爲錯誤的Python版本進行編譯,並不能因此被加載。

main.c有呼叫import_transcendentals(),它不檢查返回值,即如果導入失敗或成功。如果發生故障,get_pi()get_e()指向無效的內存位置,並嘗試調用它們會導致分段錯誤。

此外,該模塊必須位於可找到它的地方。似乎嵌入時,當前目錄不搜索python模塊。 PYTHONPATH環境變量可以更改爲包含transcendentals.so所在的目錄。

以下是將代碼嵌入到C程序並避免導入問題的方法,因爲模塊代碼鏈接到可執行文件。

基本上,缺少對PyInit_transcendentals()的調用。當用Cython函數定義public

cdef public api double get_pi(): 
... 
cdef public api double get_e(): 

文件transcendentals.h將會產生。Ç應具有包含指令

#include <Python.h> 
#include "transcendentals.h" 

,然後在main

Py_Initialize(); 
PyInit_transcendentals(); 

應該沒有#include "transcendentals_api.h"和沒有import_transcendentals()

第一個原因是,根據文檔

However, note that you should include either modulename.h or modulename_api.h in a given C file, not both, otherwise you may get conflicting dual definitions.

第二個原因是,既然transcendentals.c鏈接到節目中

gcc $(cflags) $(ldflags) transcendentals.c main.c 

沒有理由導入超越數模塊。該模塊必須被初始化,但是PyInit_transcendentals()對Python 3來說是這樣做的

+0

謝謝你的回答。但是,根據官方文件,「api」應該是「公共」的替代品。即使在官方示例中,它也不會調用PyInit_transcendentals();'。你能解釋爲什麼你的方法有效嗎? – spacegoing

+0

檢查文檔後,我認爲我的方法實際上是一種替代方法,我不確定爲什麼'import_transcendentals();'不起作用。 –

+0

我真的很感謝你的努力。最後一個問題,你能解釋我的報價嗎? – spacegoing