使用 Cython 生成的头文件

Bit*_*hyz 3 c cython

根据文档,可以使用从 Cython 生成的 C 头文件。我已经Hello World毫无问题地遵循了这个例子,现在我想尝试一些不同的东西。我想使用公共声明来使用自定义方法。我的代码结构如下:

  • 你好.pyx
  • 设置文件
  • 主文件

你好.pyx

cdef public void say_hello():
    print("Hello World")
Run Code Online (Sandbox Code Playgroud)

设置文件

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
)
Run Code Online (Sandbox Code Playgroud)

主文件

#include "hello.h"

int main(void){
    say_hello();
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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 */
Run Code Online (Sandbox Code Playgroud)

据我了解,似乎 gcc 无法获得正确版本的 Python(我使用的是 Python 3.5)。有没有办法以某种方式设置它?另外,如果确实如此,为什么在我运行命令时没有处理这个链接python3 setup.py build_ext

我对 C 没有太多经验,所以我可能会遗漏一些东西。

ead*_*ead 8

我认为问题在于您的误解,这distutils将为您构建可执行文件。事实并非如此。

Out 目标是使用 C 程序中的一些 Python 功能。让我们自己做必要的步骤:

  1. 使用 cython 生成hello.hhello.c来自给定的 pyx 文件。
  2. 使用 createdhello.h来在 C 程序中导入 python 功能 ( main.c)。
  3. 使用编译器编译文件hello.cmain.c.
  4. 使用链接器将上一步中创建的目标文件链接到可执行文件。

第一步很简单:

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


>>>python -m cython hello.pyx
Run Code Online (Sandbox Code Playgroud)

现在我们有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!
} 
Run Code Online (Sandbox Code Playgroud)

重要提示:如果您使用 Python>=3.5 和 Cython>=0.29,您必须考虑多阶段模块初始化,如本SO-post 中所述- 否则生成的程序将崩溃。

我不确定为什么 cython 的结果不包括Python.h(这将使其独立),但它就是这样 - 您需要在hello.h. 此外Py_Initialize()Py_Finalize()应该在使用hello.h.Also中的功能之前和之后调用。此外,您需要初始化模块helloinithello()否则分段错误将在可执行文件的开头困扰您。

现在我们需要找到编译的标志。这可以通过实用程序/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
Run Code Online (Sandbox Code Playgroud)

所以让我们构建它:

>>>gcc -c hello.c -o hello.o <our cflags>
>>>gcc -c main.c -o main.o <our cflags>
Run Code Online (Sandbox Code Playgroud)

现在我们有目标文件hello.omain.o我们的工作目录。我们需要链接它们,为了找到正确的标志,我们再次使用该python-config实用程序,但这次使用 - 选项--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
Run Code Online (Sandbox Code Playgroud)

这意味着:

>>> gcc main.o hello.o -o prog <our ldflags>
Run Code Online (Sandbox Code Playgroud)

现在我们终于有了我们的可执行文件!


实际上,这并不是所要求的,但是还有另一种可能性可以通过使用选项从 cython 代码生成可执行文件--embed。打开此开关后,不仅模块被 cythonized,而且主函数也被创建。为此,您hello.pyx可以如下所示:

#hello.pyx:
cdef public void say_hello():
    print("Hello World")
##main:
say_hello()
Run Code Online (Sandbox Code Playgroud)

也可以使用__name__=="__main__"技巧,但不是必需的。

现在运行后:

>>>python -m cython hello.pyx --embed
Run Code Online (Sandbox Code Playgroud)

main结果hello.c中创建了一个函数,负责设置/初始化 python 环境。所以我们可以构建并链接它:

>>> gcc hello.c -o prog <our cflags> <our ldflags>
Run Code Online (Sandbox Code Playgroud)

我们已经完成了 - 不需要知道,如何正确地初始化整个 python 东西!