2017-08-30 62 views
1

問題:用Cython:無法轉換指向Python對象模塊拆分後

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 - 不是這裏需要一大堆,實際上它只是 實施虛空*包裝的。

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

mod_one.pxd - 聲明瞭包裝類的遠端設備C結構

cimport core 

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

mod_one.pyx - 定義遠端設備包裝類。注意 __set_ptr功能,這是cdef'd,並採取了遠端設備*

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 - 聲明頻道包裝類,這是我,因爲它是在函數中使用我只在這裏展示 那正在造成我的問題。

cimport core 
cimport mod_one 

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

mod_two.pyx - 定義頻道包裝類並聲明 Py_GetRemoteDevice函數,它從C庫包裝了GetRemoteDevice函數 。

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文檔和Google ,但到目前爲止,我還沒有找到任何答案來解決我的問題。

謝謝,請讓我知道是否需要任何額外的信息!

+0

看起來像你需要'ret_val'上的類型註釋嗎? – chrisb

+0

@chrisb,我想知道那件事,但是我一直無法使它工作。可能是因爲我不確定它應該是什麼樣子。我試過 mod_one.PyRemoteDevice ret_val = mod_one.PyRemoteDevice() 沒有運氣。我會再試一次,只是爲了確保。 – rahvin74

+0

好吧@micrisb,你是對的。只要我輸入我最後的評論,我意識到我的錯誤。我需要'cdef'在我最後發佈的內容前面,所以它會是'cdef mod_one.PyRemoteDevice ret_val = mod_one.PyRemoteDevice()'。我感覺有點傻!如果您將您的評論置於答案中,我會接受它。 – rahvin74

回答

0

訪問擴展類型的c級函數需要cython知道確切的類型,否則它被視爲一個通用的Python對象。所以需要這樣的類型註釋

cdef mod_one.PyRemoteDevice ret_val = ... 
+0

謝謝@chrisb,你是一位紳士和學者! – rahvin74