2016-06-23 48 views
10

Sto scrivendo un'interfaccia di alto livello per una libreria C per Python che usa Cython.
Ho un'estensione Tipo A che inizializza la libreria con un puntatore a una struttura di contesto C più complessa c_context. Il puntatore viene salvato in A.
A ha anche una funzione def che a sua volta crea un'altra estensione Tipo B inizializzando un'altra struttura C con una chiamata di funzione di libreria. Questa struttura è necessaria per le successive chiamate di libreria effettuate in B.
B bisogno il puntatore c_context da A che è avvolto da me entro il tipo di estensione py_context per passare alla __cinit__ da B:Come scrivere un wrapper Python completo attorno a C Struct usando Cython?

#lib.pxd (C library definitions) 
cdef extern from "lib.h": 
    ctypedef struct c_context: 
     pass 

#file py_context.pxd 
from lib cimport c_context 

cdef class py_context: 
    cdef c_context *context 
    cdef create(cls, c_context *context) 
    cdef c_context* get(self) 

#file py_context.pyx 
def class py_context: 
    @staticmethod 
    cdef create(cls, c_context *c): 
     cls = py_nfc_context() 
     cls.context = c 
     return cls 

    cdef c_context* get(self): 
     return self.context 

Passando l'involucro con il contesto C corretto funziona perfettamente.

Ora devo rimuovere di nuovo la struttura C da py_context e salvarla in B. Ho aggiunto cdef c_context get(self) a py_context.pxd/pyx. Chiamata py_context.get() da Bs __cinit__ risultati in: AttributeError: py_context object has no attribute get.

sembra che non ho ricevuto la mia testa intorno quando chiamare cdef funzioni Cython.

Quindi la mia domanda è: Qual è il modo migliore per estrarre di nuovo la struct C dalla mia classe wrapper?

+0

Hai letto questo? http://docs.cython.org/src/tutorial/clibraries.html – chrisb

risposta

6

Il problema è che Cython non conosce il tipo di dati della variabile py_context in fase di compilazione. Le chiamate alle funzioni cdef vengono risolte al momento della compilazione e non esiste alcun meccanismo per individuarlo in fase di esecuzione dalla ricerca di attributi (come con le normali funzioni Python).

[Si noti che def funzioni scritte entro Cython sono ancora compilati e possibile specificare i tipi di dati, quindi sono perfettamente in grado di chiamare cdef funzioni se hanno le giuste informazioni.]

non dare il codice rilevante nel quale sta andando male (il costruttore di tipo B), ma ecco un esempio molto semplificato che si spera darvi un paio di modi per risolvere il problema:

cdef class A: 
    cdef f(self): 
     return 

def f1(var): 
    var.f() 

#f1(A()) # will fail at runtime with an attribute error 

Nel f1 il tipo di var è sconosciuto, e quindi si può Chiamare le funzioni cdef.

def f2(A var): 
    var.f() 

f2(A()) # will work 
f2(1) # will fail, int can't be converted to A 

In f2 il tipo di var è vincolata ad essere A, e quindi possono tranquillamente chiamare cdef funzioni associate A. Se si passa qualcosa che non è A ad esso si otterrà un TypeError in fase di esecuzione.

def f3(var): 
    cdef A another_reference_to_var = var # this does test that the types match 
    another_reference_to_var.f() 

f3(A()) # will work 
f3(1) # will fail, int can't be converted to A 

La funzione f3 può prendere una variabile di qualsiasi tipo.Tuttavia, quando lo assegni a another_reference_to_var che è cdef ed è uno A, controlla che il tipo corrisponda (e solleva un'eccezione di runtime se non lo fa). Poiché another_reference_to_var è noto per essere A in fase di compilazione, è possibile chiamare le funzioni cdef s.

In sostanza, è necessario specificare il tipo di input rilevante per la funzione __cinit__.

+2

+1 Questa è una risposta migliore, quindi ho prontamente rimosso il mio poiché era un po 'fuorviante. [Qui] (https://github.com/cythonbook/examples/tree/master/07-wrapping-c/02-wrapping-c-structs-mt-random) è un url con un esempio completo di wrapping di una struct per OP per vedere se ha bisogno. –

+0

Funziona come un fascino. Le informazioni sul tipo mancante in Bs '__cinit__' era il problema. L'aggiunta di 'py_context' risolve il problema. Le tue informazioni dovrebbero essere aggiunte ai documenti Cython poiché sono un po 'vaghe a volte. –