2012-01-10 16 views
5

Sto provando a scrivere un wrapper attorno a un programma C in modo da poterlo chiamare da Python. Sto usando Cython per farlo. La funzione C accetta una funzione di callback come argomento, ma questa funzione di richiamo sarà conosciuta solo al momento dell'esecuzione del programma python. Sono stato alla ricerca come fare questo e sembra non ci sia una soluzione semplice, ma il seguente sembra funzionare:Come passare un puntatore a funzione a un programma esterno in Cython

#python.py 

libc = cdll.LoadLibrary("myfunc.so") #Callback function is defined in myfunc.so 
.... 
c_wrapper(libc.fun, ...) 

.

#c_wrapper.pyx 

cdef extern void mainfunction(void *F, ...) #The intial C function we are wrapping 
ctypedef void (*myfuncptr)() 

def c_wrapper(f, ...) # our function pointer is passed to the wrapper as a Python object 
    cdef myfuncptr thisfunc 
    thisfunc = (<myfuncptr*><size_t>addressof(f))[0] 
    mainfunction(thisfunc, ...) 

Questo metodo funziona per funzioni C e Fortran (Io parto dal presupposto che il lavoro per la maggior parte languges compilati) e le funzioni Python (utilizzando i tipi C), ma sembra un po 'imbarazzante. C'è un modo più semplice per farlo in Cython?

Grazie

EDIT: Non sono in grado di cambiare la libreria C che sto cercando di avvolgere

+0

It w Sarebbe bello se ce n'è uno. Ma se non ne ottieni nessuna, la tua soluzione proposta potrebbe essere utile ad altre persone. In tal caso, forse potresti riformularlo come una coppia di domande/soluzioni? – dsign

+0

Ok, una cosa che non ho ottenuto la prima volta, e quindi la mia risposta: la funzione di callback può provenire da una libreria C/FORTRAN o può essere una funzione nativa di Python. È questo il caso? –

+0

@ Ricardo, quello è corretto. Spiacente, potrei non averlo dichiarato esplicitamente. Basilea, se la funzione proviene da una libreria C/FORTRAN è memorizzata come oggetto _FuncPtr, se la funzione è scritta in Python nativo può essere memorizzata come CFunctionType (usando i tipi C). Suppongo che quello che sto chiedendo veramente è se c'è un modo ellegante per lanciare qualsiasi/tutti questi in un puntatore di funzione C all'interno del wrapper? – SimpleSimon

risposta

5

immagino siete a conoscenza di this?

E 'possibile chiamare il mio codice Python dal C?

Risposta: Sì, facilmente. Seguire l'esempio di Demos/richiamata/nella distribuzione Cython fonte

Quindi, sapendo che si può DLOAD tua funzione principale, diamo un altro approccio. Ho scritto un paio di funzioni stupide per testare questo:

/* lib.c -> lib.so */ 
#include <stdio.h> 

void fn1(void) { 
     puts("Called function 1"); 
} 

void fn2(void) { 
     puts("Called function 2"); 
} 

poi, la funzione che prende il callback

/* main.c -> main.so */ 

typedef void (*callback)(); 

void mainfunction(void *F) { 
     ((callback)F)(); 
} 

che può essere trasmesso direttamente da Python:

>>> from ctypes import cdll 
>>> lib = cdll.LoadLibrary('./lib.so') 
>>> main = cdll.LoadLibrary('./main.so') 
>>> main.mainfunction(lib.fn1) 
Called function 1 
>>> main.mainfunction(lib.fn2) 
Called function 2 

Ed ora , includiamo una funzione Python:

>>> from ctypes import CFUNCTYPE 
>>> def pyfn(): 
...  print "Called the Python function" 
... 
>>> CWRAPPER = CFUNCTYPE(None) 

>>> wrapped_py_func = CWRAPPER(pyfn) 
>>> main.mainfunction(wrapped_py_func) 
Called the Python function 
+0

Grazie a @Ricardo Cárdenes, Ho visto questo esempio, mostra come avvolgere un'API C con un'interfaccia di callback, in modo da poter passare le funzioni Python ad esso come richiami. Questo è un buon esempio di come questo problema potrebbe essere risolto se avessi accesso alla libreria C che sto cercando di avvolgere, sfortunatamente non riesco a toccarlo. Grazie – SimpleSimon

+0

Mh ... Credo di aver frainteso il tuo intento, quindi ... –

+0

@Dan: nuova risposta, basata su altre ipotesi. –

Problemi correlati