2017-07-31 101 views
0

根據documentation,可以使用從Cython生成的C頭文件。我按照Hello World的例子沒有問題,現在我想嘗試一些不同的東西。我想使用公共聲明來使用自定義方法。我的代碼結構如下:使用Cython生成的頭文件

  • hello.pyx
  • setup.py
  • 的main.c

hello.pyx

cdef public void say_hello(): 
    print("Hello World") 

setup.py

from distutils.core import setup 
from distutils.extension import Extension 
from Cython.Distutils import build_ext 

ext_modules = [ 
    Extension("hello", ["hello.pyx", "main.c"]), 
] 

setup(
    name='Hello app', 
    cmdclass={'build_ext': build_ext}, 
    ext_modules=ext_modules 
) 

的main.c

#include "hello.h" 

int main(void){ 
    say_hello(); 
} 

main.c充當測試文件以驗證say_hello()方法按預期工作。 構建設置文件python3 setup.py build_ext會生成以下輸出。

running build_ext 
    skipping 'hello.c' Cython extension (up-to-date) 
    building 'hello' extension 
    x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/python3.5m -c hello.c -o build/temp.linux-x86_64-3.5/hello.o 
    x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/python3.5m -c main.c -o build/temp.linux-x86_64-3.5/main.o 
    In file included from main.c:1:0: 
    hello.h:26:1: error: unknown type name ‘PyMODINIT_FUNC’ 
    PyMODINIT_FUNC inithello(void); 
    ^
    error: 

command 'x86_64-linux-gnu-gcc' failed with exit status 1 

的hello.h文件包含以下

/* Generated by Cython 0.25.2 */ 

#ifndef __PYX_HAVE__hello 
#define __PYX_HAVE__hello 


#ifndef __PYX_HAVE_API__hello 

#ifndef __PYX_EXTERN_C 
    #ifdef __cplusplus 
    #define __PYX_EXTERN_C extern "C" 
    #else 
    #define __PYX_EXTERN_C extern 
    #endif 
#endif 

#ifndef DL_IMPORT 
    #define DL_IMPORT(_T) _T 
#endif 

__PYX_EXTERN_C DL_IMPORT(void) say_hello(void); 

#endif /* !__PYX_HAVE_API__hello */ 

#if PY_MAJOR_VERSION < 3 
PyMODINIT_FUNC inithello(void); // <-- Line 26 
#else 
PyMODINIT_FUNC PyInit_hello(void); 
#endif 

#endif /* !__PYX_HAVE__hello */ 

據我瞭解,似乎在GCC不能夠得到的Python的正確版本(我使用Python 3.5)。有什麼辦法可以設置嗎?另外,如果確實如此,當我運行python3 setup.py build_ext命令時,爲什麼不將鏈接到

我沒有太多的C經驗,所以我可能會錯過一些東西。

回答

1

我認爲這個問題是你的誤解,distutils會爲你建立可執行文件。事實並非如此。

我們的目標是使用C程序中的一些python功能。讓我們自己做的必要步驟:

  1. 使用用Cython產生從給定的PYX文件hello.hhello.c
  2. 使用創建的hello.h在C程序(main.c)中導入python功能。
  3. 使用編譯器編譯文件hello.cmain.c
  4. 使用鏈接器將上一步創建的目標文件鏈接到可執行文件。

第一步很簡單:

​​

現在,我們在我們的工作目錄有hello.chello.h。你main.c是錯誤的,應該如下所示:

//main.c 
#include <Python.h> //needed 
#include "hello.h" 

int main(void){ 
    Py_Initialize(); //Needed! 
    inithello();  //Needed! called PyInit_hello() for Python3 
    say_hello(); 
    Py_Finalize(); //Needed! 
} 

我不知道爲什麼用Cython的結果不包括Python.h(這將使它自成一體),但是這是它的方式是 - 你需要在hello.h之前包括它。 Py_Initialize()Py_Finalize()應該在使用hello.h功能之前和之後調用。您還需要使用inithello()初始化模塊hello,否則在可執行文件開始時會出現分段錯誤。

現在我們需要找到用於編譯的標誌。這可以與公用事業/usr/bin/python-config(我使用python2.7,你需要爲python3這樣做)和選件--cflags來完成:

>>> /usr/bin/python-config --cflags 
-I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7 
-fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong 
-Wformat -Werror=format-security -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes 

那麼讓我們來構建它:

>>>gcc -c hello.c -o hello.o <our cflags> 
>>>gcc -c main.c -o main.o <our cflags> 

現在我們有目標文件hello.omain.o在我們的工作目錄中。我們需要將它們鏈接,併爲了再次找到我們使用正確的標誌的工具,但這次與--ldflags - 選項:

>>> /usr/bin/python-config --ldflags 
--L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -lpython2.7 
-lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions 

這意味着:

>>> gcc main.o hello.o -o prog <our ldflags> 

而現在我們有終於可執行文件了


其實,這不正是被詢問,但還有另一種可能產生從用Cython代碼的可執行文件,通過選項--embed。通過此開關,不僅模塊被cython化,而且還創建一個主要功能。對於此您可以hello.pyx如下所示:

#hello.pyx: 
cdef public void say_hello(): 
    print("Hello World") 
##main: 
say_hello() 

很可能也使用__name__=="__main__"伎倆,但沒有必要。

運行後,現在:

>>>python -m cython hello.pyx --embed 

中所產生的hello.c這需要建立/初始化蟒蛇環境的關懷創建main功能。所以,我們纔可以建立並鏈接:

>>> gcc hello.c -o prog <our cflags> <our ldflags> 

而且我們正在這樣做 - 沒有必要知道,如何正確的初始化整個蟒蛇,東西!

+0

感謝您的回答@ead,找到標誌的提示非常有用。 唉,在'main.c'的編譯過程中,我得到了方法'inithello()'的隱式聲明警告,後來在我嘗試鏈接時拋出一個錯誤「未定義的inithello引用」。 使用Python2.7很好! – BitWhyz

+0

@BitWhyz通過查看hello.h代碼,我認爲你應該在Python3中使用'PyInit_hello()'而不是'inithello()'。 – ead

+0

它的工作!謝謝。我不明白爲什麼文檔沒有提到這一點。我會很感激挖掘更多的好資源。 – BitWhyz