【问题标题】:Use generated header file from Cython使用 Cython 生成的头文件
【发布时间】:2017-07-31 15:13:07
【问题描述】:

根据documentation,可以使用从 Cython 生成的 C 头文件。我按照Hello World 的例子没有问题,现在我想尝试一些不同的东西。我想使用公共声明来使用自定义方法。我的代码结构如下:

  • 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
)

ma​​in.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 没有太多经验,所以我可能会遗漏一些东西。

【问题讨论】:

    标签: c cython


    【解决方案1】:

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

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

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

    第一步很简单:

    #hello.pyx:
    cdef public void say_hello():
        print("Hello World")
    
    
    >>>python -m cython hello.pyx
    

    现在我们的工作目录中有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!
    } 
    

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

    我不确定为什么 cython 的结果不包含 Python.h(这将使其独立),但它就是这样 - 您需要在 hello.h 之前包含它。还应该在使用hello.h 的功能之前和之后调用Py_Initialize()Py_Finalize()。此外,您需要使用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。我们需要链接它们,为了找到正确的标志,我们再次使用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
    

    这意味着:

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

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


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

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

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

    现在运行后:

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

    main 函数在生成的 hello.c 中创建,它负责设置/初始化 python 环境。所以我们可以构建和链接它:

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

    我们已经完成了 - 不需要知道如何初始化整个 python-stuff!

    【讨论】:

    • 感谢您的回答@ead,查找标志的提示非常有用。唉,在main.c 的编译过程中,我收到方法inithello() 的隐式声明警告,后来在我尝试链接时抛出错误undefined reference to inithello。使用 Python2.7 效果很好!
    • @BitWhyz 通过查看 hello.h 代码,我认为您应该在 Python3 中使用 PyInit_hello() 而不是 inithello()
    • 成功了!谢谢你。我不明白为什么文档没有提到这一点。我会很感激一个很好的资源来挖掘更多。
    • @BitWhyz 编写好的文档非常困难,甚至比编写好的代码更难。我认为 cython 的人做得不错,但是版本之间发生了很多变化,这并不容易跟踪。
    【解决方案2】:

    您可以使用api 关键字

    cdef api void hello():
        print("hello")
        return;
    

    这将在编译时自动生成一个“hello.h”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多