2017-08-30 1 views
1

Le problème:Cython: ne peut pas convertir pointeur vers l'objet Python après le module partagé

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 

Contexte: J'ai une bibliothèque C assez grand, je suis emballage avec Cython. Initialement, j'ai avait tout dans deux fichiers, core.pyx et core.pxd. Cependant, plus la bibliothèque encapsulée est grande, plus le fichier core.c généré est grand. 2,5 millions de lignes et de comptage. L'heure de compilation et l'utilisation de la mémoire sont devenues lourdes . J'ai décidé de diviser les choses en plusieurs fichiers .pyx. C'est alors que les choses qui ont utilisé ont cessé de fonctionner.

Avertissement: Veuillez garder à l'esprit que j'ai grandement simplifié les choses pour cette question. La bibliothèque encapsulée est assez grande et propriétaire.

Les détails:
core.pxd - contient tous les composants nécessaires à la bibliothèque C
J'emballage. Aussi une simple déclaration de classe wrapper 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 - pas beaucoup est nécessaire ici, vraiment est juste la mise en œuvre de l'emballage Void *.

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

mod_one.pxd - déclare la classe wrapper pour le RemoteDevice C struct

cimport core 

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

mod_one.pyx - définit la classe wrapper RemoteDevice. Notez la fonction __set_ptr, qui est cdef'd et prend une 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 - déclare la classe wrapper de canal, que je ne montrant ici parce qu'il est utilisé dans la fonction me cause des problèmes.

cimport core 
cimport mod_one 

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

mod_two.pyx - définit la classe wrapper Channel, et déclare la fonction de Py_GetRemoteDevice, qui enveloppe la fonction GetRemoteDevice de la bibliothèque 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 

Le problème que je reçois est que lorsque Cythonizing mod_two, je reçois le erreur suivant:

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 

Je suis un peu confus, parce que cela utilisé pour travailler quand j'avais tout un module .Une différence que je peux voir est que toutes mes classes wrapper étaient entièrement définies dans core.pyx parce que je n'avais pas besoin de les partager entre les modules. Maintenant, je dois les partager, donc je les ai séparés en fichiers .pyx et .pxd.

Quelqu'un peut-il me diriger dans la bonne direction? J'ai parcouru les docs Cython et Google mais jusqu'ici je n'ai rien trouvé qui réponde à ma question.

Merci, et s'il vous plaît laissez-moi savoir si des informations supplémentaires sont nécessaires!

+0

Vous avez besoin d'une annotation de type sur 'ret_val'? – chrisb

+0

@ chrisb, je me demandais à ce sujet mais je n'ai pas réussi à le faire fonctionner. Probablement parce que je ne suis pas sûr de ce à quoi ça devrait ressembler. J'ai essayé mod_one.PyRemoteDevice ret_val = mod_one.PyRemoteDevice() sans aucune chance. Je vais essayer encore, juste pour être sûr. – rahvin74

+0

Ok, @ chrisb, vous avez raison. Dès que j'ai tapé mon dernier commentaire, j'ai réalisé mon erreur. J'avais besoin de 'cdef' devant ce que j'ai posté en dernier, donc ce serait' cdef mod_one.PyRemoteDevice ret_val = mod_one.PyRemoteDevice() '. Je me sens un peu bête! Si vous faites votre commentaire dans une réponse, je l'accepterai. – rahvin74

Répondre

0

L'accès aux fonctions de niveau c sur les types d'extension nécessite que Cython connaisse le type exact, sinon il est traité comme un objet Python générique. Donc une annotation de type comme ceci est nécessaire

cdef mod_one.PyRemoteDevice ret_val = ... 
+0

Merci @chrisb, vous êtes un Gentleman et un Scholar! – rahvin74