2017-07-31 29 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