2012-07-28 18 views
5

Tengo la siguiente pregunta. Tenemos que pasar funciones de devolución de llamada al código C. Si la función es una función Cython en el mismo módulo, la situación es bastante simplePython/Cython/C y devoluciones de llamada, llamando a una función de Python desde C usando Cython

En Cython:

def callme(int x): 
    c_callme(x, <int (*)(int)>&callbackme) 

cdef int callbackme(int x): 
    print <int> x 
    return <int>x 

En C:

int c_callme(int x, int (*f)(int)) 
{ 
    printf("---%d\n",x); 
    printf("--%d\n",f(x)); 
    return x; 
} 

La pregunta es la siguiente: queremos generalice este código de la manera más Pythonic para que pueda aceptar también las funciones de Python como argumentos de devolución de llamada (por supuesto, se necesita alguna capa adicional), y también las funciones C/Cython de otro módulo. Supongo que para las funciones C/Cython de un módulo separado, uno tiene que obtener la dirección de estas funciones (convertir a int largo) y para la función Python se necesita un determinado contenedor

+0

por favor, lea las preguntas frecuentes acerca de formato de código ... – ThiefMaster

Respuesta

0

Creo que la manera más fácil sería envolverlo C devoluciones de llamada en

cdef class Callback(object): 
    cdef int (*f)(int)  # I hope I got the syntax right... 

    def __call__(int x): 
     return self.f(x) 

por lo que pueden pasar objetos de este tipo, así como cualquier otro exigible, a la función que debe llamar a la devolución de llamada.

Sin embargo, si debe llamar a la devolución de llamada desde C, entonces debe pasarle un argumento adicional, es decir, la dirección opcional de un objeto de Python, tal vez emitida a `void *.

(Por favor, no punteros de función fundido a long, o puede tener un comportamiento indefinido. No estoy seguro de si se puede transmitir de manera segura un puntero de función a cualquier cosa, incluso void*.)

+0

también estaba pensando en esa dirección. Pero el código no compila en absoluto :(. ¿Puede profundizar más en por qué no tenemos que lanzar punteros a largo? –

+0

@IvanOseledets: no se garantiza que los valores del puntero a la función encajen en 'long', y viceversa. podría funcionar en una máquina, pero falla en otra. Que este código no se compila probablemente se deba a un error de sintaxis, no tengo una instalación de Cython a mano. –

9

En este ejemplo , extracted from a Python wrapper al Cubature integration C library, una función Python se pasa a una función C que tiene el prototipo dado por cfunction. Se puede crear una función con el mismo prototipo, llamado cfunction_cb (devolución de llamada), y devolver el mismo tipo, int en este ejemplo):

cdef object f 
ctypedef int (*cfunction) (double a, double b, double c, void *args) 

cdef int cfunction_cb(double a, double b, double c, void *args): 
    global f 
    result_from_function = (<object>f)(a, b, c, *<tuple>args) 
    for k in range(fdim): 
     fval[k] = fval_buffer[k] 
    return 0 

Cuando se llama a la función C, puede emitir su envoltorio de devolución de llamada utilizando el C prototipo:

def main(pythonf, double a, double b, double c, args): 
    global f 
    f = pythonf 
    c_function(<cfunction> cfunction_cb, 
       double a, 
       double b, 
       double c, 
       <void *> args) 

en este ejemplo también se muestra cómo pasar argumentos a su función de Python, a través de C.

+1

esto debe aceptarse como respuesta, funciona bien. instancia mantiene viva y bien la instancia. – dashesy

Cuestiones relacionadas