Cython:模块拆分后无法将指针转换为Python对象

时间:2017-08-30 22:22:15

标签: python c cython

问题:

Error compiling Cython file:
------------------------------------------------------------
...
cpdef Py_GetRemoteDevice(PyChannel Chan):
    ret_val = mod_one.PyRemoteDevice()
    cdef core.RemoteDevice  * tmp
    with nogil:
        tmp = core.GetRemoteDevice(Chan.__instance)
    ret_val.__set_ptr(tmp)
                        ^
------------------------------------------------------------

core/mod_two.pyx:11589:25: Cannot convert 'RemoteDevice *' to Python object

背景: 我有一个相当大的C库我用Cython包装。最初我 拥有两个文件中的所有内容,core.pyx和core.pxd。但是,越多 包含较大的core.c文件生成的库。 250万行和计数。编译时间和内存使用量变为 繁琐。

我决定将内容分成多个.pyx文件。当使用的东西时 工作停止了工作。

声明: 请记住,我已经大大简化了这个问题。 包装的库有点大而且专有。

详细信息:
core.pxd - 包含C库中的所有必要组件
我是包装的。也是一个简单的Void *包装类声明。

cdef class Void:
    cdef void *__void
    cdef __set_ptr(self, void *ptr)

cdef extern from "core.h":
    ctypedef unsigned char U8
    ctypedef unsigned int U32

    void OS_MemSet(U8 *dest, U8 byte, U32 len) nogil

cdef extern from "mod_one.h":
    cdef struct _RemoteDevice
    ctypedef _RemoteDevice RemoteDevice

cdef extern from "mod_two.h":
    cdef struct _Channel
    ctypedef _Channel Channel

core.pyx - 这里不需要很多,真的只是 实现Void *包装器。

cdef class Void:
    cdef __set_ptr(self, void *ptr):
        self.__void = ptr

mod_one.pxd - 声明RemoteDevice C结构的包装类

cimport core

cdef class PyRemoteDevice:
    cdef core.RemoteDevice *__instance
    cdef __set_ptr(self, core.RemoteDevice *ptr)

mod_one.pyx - 定义RemoteDevice包装类。请注意 __set_ptr函数,即cdef' d并采用RemoteDevice *

from cpython.mem cimport PyMem_Malloc, PyMem_Free
import core
cimport core

cdef class PyRemoteDevice:
    def __cinit__(self):
        self.__instance = <core.RemoteDevice *>PyMem_Malloc(sizeof(core.RemoteDevice))

    def __init__(self):
        core.OS_MemSet(<core.U8 *>self.__instance, <core.U8>0, sizeof(core.RemoteDevice))

    cdef __set_ptr(self, core.RemoteDevice *ptr):
        self.__instance = ptr

    def __dealloc__(self):
        if self.__instance is not NULL:
            PyMem_Free(self.__instance)
            self.__instance = NULL

mod_two.pxd - 声明了Channel包装器类,我只展示了它 这是因为它在导致我出问题的函数中使用了它。

cimport core
cimport mod_one

cdef class PyChannel:
    cdef core.Channel *__instance
    cdef __set_ptr(self, core.Channel *ptr)

mod_two.pyx - 定义Channel包装器类,并声明 Py_GetRemoteDevice函数,它包装了GetRemoteDevice函数 来自C库。

from cpython.mem cimport PyMem_Malloc, PyMem_Free import core cimport core import mod_one cimport mod_one

cdef class PyChannel:
    def __cinit__(self):
        self.__instance = <core.Channel *>PyMem_Malloc(sizeof(core.Channel))

    def __init__(self):
        core.OS_MemSet(<core.U8 *>self.__instance, <core.U8>0, sizeof(core.Channel))

    cdef __set_ptr(self, core.Channel *ptr):
        self.__instance = ptr

    def __dealloc__(self):
        if self.__instance is not NULL:
            PyMem_Free(self.__instance)
            self.__instance = NULL

cpdef Py_GetRemoteDevice(PyChannel Chan):
    ret_val = mod_one.PyRemoteDevice()
    cdef core.RemoteDevice  * tmp
    with nogil:
        tmp = core.GetRemoteDevice(Chan.__instance)
    ret_val.__set_ptr(tmp)
    return ret_val

我遇到的问题是,当Cythonizing mod_two时,我得到了 以下错误:

Error compiling Cython file:
------------------------------------------------------------
...
cpdef Py_GetRemoteDevice(PyChannel Chan):
    ret_val = mod_one.PyRemoteDevice()
    cdef core.RemoteDevice  * tmp
    with nogil:
        tmp = core.GetRemoteDevice(Chan.__instance)
    ret_val.__set_ptr(tmp)
                        ^
------------------------------------------------------------

core/mod_two.pyx:11589:25: Cannot convert 'RemoteDevice *' to Python object

我有点困惑,因为当我把所有东西都放在一个时,这曾经有用了 模块。我能看到的一个区别是我的所有包装类都是完全定义的 在core.pyx中,因为我不需要在模块之间共享它们。现在 我需要分享它们,所以我将它们分成.pyx和.pxd文件。

有人能指出我正确的方向吗?我已经搜过了Cython文档和谷歌 但到目前为止,我还没有找到任何可以回答我问题的内容。

谢谢,如果需要任何其他信息,请告诉我们!

1 个答案:

答案 0 :(得分:0)

在扩展类型上访问c级函数需要cython知道确切的类型,否则它被视为通用Python对象。所以需要像这样的类型注释

cdef mod_one.PyRemoteDevice ret_val = ...