使用Cython生成的头文件

时间:2017-07-31 15:13:08

标签: c cython

根据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
)

的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经验,所以我可能会遗漏一些东西。

2 个答案:

答案 0 :(得分:4)

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

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

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

    #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!
    } 
    

    我不确定为什么cython的结果不包含Python.h(这会使它自包含),但这就是它的样子 - 你需要在{{{{{{ 1}}。在使用hello.h功能之前和之后,也应调用Py_Initialize()Py_Finalize()。此外,您需要使用hello.h初始化模块hello,否则在可执行文件的开头,分段错误会困扰你。

    现在我们需要找到用于编译的标志。这可以使用实用程序inithello()(我使用python2.7,你需要对python3执行相同操作)和选项/usr/bin/python-config

    --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.o。我们需要链接它们,以便找到我们再次使用main.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> ,还有另一种可能性从cython代码生成可执行文件。通过此开关,不仅可以对模块进行cython化,还可以创建主要功能。为此,您的--embed可能如下所示:

    hello.pyx

    也可以使用#hello.pyx: cdef public void say_hello(): print("Hello World") ##main: say_hello() 技巧,但不需要。

    现在运行后:

    __name__=="__main__"

    在生成的>>>python -m cython hello.pyx --embed 中创建main函数,负责设置/初始化python环境。所以我们可以构建并链接它:

    hello.c

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

答案 1 :(得分:0)

您可以使用api关键字

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

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