2011-03-09 22 views
8

He estado trabajando con Cython en un intento de interactuar con una biblioteca escrita en C++. Hasta ahora las cosas están yendo bastante bien, y puedo usar efectivamente las funciones de MOST dentro de la biblioteca. Mi único problema radica en implementar callbacks. La biblioteca cuenta con 4 definiciones de funciones que se ven un poco algo como esto:Cython: implementar devoluciones de llamada

typedef void (*Function1)(const uint16_t *data, 
         unsigned width, unsigned height); 
void SetCallBack(Function1); 

que las apliquen pensé que podría hacer algo como esto con Cython:

ctypedef void (*Function1)(unsigned short *data, 
         unsigned width, unsigned height); 
cdef extern from "lib.hpp": 
    void SetCallBack(Function1) 

Que en realidad se compila correctamente, sin embargo, No puedo, por mi propia vida, pensar cómo implementarlo de tal manera que la devolución de llamada funcione. La primera vez que intenté crear una función que acaba de llamar a que, de forma similar a la forma en que lo haría para cualquier otra función, empezar con esto:

def PySetCallBack(Func): 
    SetCallBack(Func) 

pero eso me da la (predecible) Error:

"No se puede convertir el objeto de Python a 'Function1'"

así que sí, ahí es donde estoy. Si alguien tiene experiencia en configurar devoluciones de llamadas en Cython, le agradecería cualquier ayuda. Gracias.

Editar: Siguiendo su consejo, he creado una función intermedia con un cdef, que se ve así:

cdef void cSetCallBack(Function1 function): 
    SetCallBack(function) 

Esto me parece haber conseguido ... Más cerca? Conseguir un error diferente ahora por lo menos:

error: invalid conversion from ‘void (*)(short unsigned int*, unsigned int, unsigned int)’ to ‘void (*)(const uint16_t*, unsigned int, unsigned int)’ 

Ahora, lo que puedo decir a esos tipos son idénticos, por lo que no puedo entender lo que está pasando.

Edit2: Corregido el problema al declarar un nuevo typedef:

ctypedef unsigned short uint16_t 

y usar eso como argumento para llamar, pero al parecer eso no era en realidad para llegar más cerca, pero sólo teniendo a mi alrededor una pista lateral, ya que cuando intento llamar a esa función, obtengo el mismo error "No se puede convertir el objeto Python en 'Función1'" de nuevo.

Por lo tanto, estoy más o menos donde comencé. Lo único que puedo hacer ahora es convertir explícitamente el objeto python en una función c como esa, pero, para ser sincero, no tengo idea de cómo lo haría.

Editar la tercera: bien, después de la disección de su respuesta que finalmente lo consigue, y funciona, por lo hurra y otras cosas. Lo que terminé haciendo fue la creación de una función como esta:

cdef void cSetCallback(Function1 function): 
    SetCallback(function) 
cdef void callcallback(const_ushort *data, unsigned width, unsigned height): 
    global callbackfunc 
    callbackfunc(data,width,height) 
cSetCallback(callcallback) 
def PySetCallback(callbackFunc): 
    global callbackfunc 
    callbackfunc = callbackFunc 

Así que ahora el único problema es que no puede convertir const_ushort * datos en un objeto de Python, pero ese es otro problema por completo, así que supongo que éste está resuelto, muchas gracias.

+0

Cambiar 'short unsigned int *' a 'const short unsigned int *'. – aschepler

+1

Según mi leal saber y entender, Cython no tiene idea de qué significa const, y cada vez que intento usarlo, obtengo "Const no es un identificador de tipo". – Josiah

Respuesta

5

Si se puede modificar la biblioteca para definir:

typedef void (*Function1)(const uint16_t *data, 
          unsigned width, unsigned height, 
          void *user_data); 
void SetCallBack(Function1, void*); 

lugar, me temo que no están de suerte. Si tiene el void*, entonces defina una función que llame a un objeto llamable de python con los argumentos correctos y SetCallBack con esta función y el pitón invocable.

Si no puede, pero la devolución de llamada es global (parece ser), puede crear una variable global para almacenar el objeto python. De nuevo crearía una función para llamar al objeto python y pasar a SetCallBack y su PySetCallback simplemente establecería el global y garantizaría que se registra la función adecuada.

Si la devolución de llamada es específica del contexto, pero no tiene manera de pasarle un puntero de "datos de usuario", me temo que no tiene suerte aquí.

Conozco Python y C++, pero no cython, por lo que no sé si puede crear la función en cython, o si tendría que escribir en C++.

8

Recientemente he estado en una situación donde también tuve que conectar una biblioteca de C++ existente con Python usando Cython, haciendo un uso intensivo de eventos/devoluciones de llamada. No era tan fácil de encontrar fuentes acerca de esto y me gustaría poner todo esto junto aquí:

En primer lugar, el ++ clase envoltorio C devolución de llamada (basado en 'doble (MÉTODO) (void)' prototipo , pero podría haber sido a plantillas, ya que puede manejar las plantillas Cython):

ALabCallBack.h:

#ifndef ALABCALLBACK_H_ 
#define ALABCALLBACK_H_ 


#include <iostream> 

using namespace std; 

namespace elps { 

//template < typename ReturnType, typename Parameter > 
class ALabCallBack { 
public: 

    typedef double (*Method)(void *param, void *user_data); 

    ALabCallBack(); 
    ALabCallBack(Method method, void *user_data); 
    virtual ~ALabCallBack(); 

    double cy_execute(void *parameter); 

    bool IsCythonCall() 
    { 
     return is_cy_call; 
    } 

protected: 

    bool is_cy_call; 

private: 

    //void *_param; 
    Method _method; 
    void *_user_data; 

}; 


} /* namespace elps */ 
#endif /* ALABCALLBACK_H_ */ 

ALabCallBack.cpp:

#include "ALabCallBack.h" 

namespace elps { 


ALabCallBack::ALabCallBack() { 
    is_cy_call = true; 
}; 

ALabCallBack::~ALabCallBack() { 
}; 

ALabCallBack::ALabCallBack(Method method, void *user_data) { 
    is_cy_call = true; 
    _method = method; 
    _user_data = user_data; 
}; 

double ALabCallBack::cy_execute(void *parameter) 
{ 
    return _method(parameter, _user_data); 
}; 


} /* namespace elps */ 

Dónde:

  • 'callback' :: El método de patrón/convertidor para disparar un método objeto desde C Python (= Method) mecanografiadas informaciones sobre

  • 'método' :: El método eficaz pasado por el usuario Python (= user_data)

  • 'parámetro' :: el parámetro que se pasa a la 'método'

Ahora, tenemos que implementar el archivo .pyx ...

Nuestro prototipo de base:

ctypedef double (*Method)(void *param, void *user_data) 

A continuación, ofrecemos un envoltorio Cython para la clase C++:

cdef extern from "../inc/ALabCallBack.h" namespace "elps" : 
    cdef cppclass ALabCallBack: 
     ALabCallBack(Method method, void *user_data) 
     double cy_execute(void *parameter) 

El método de patrón/convertidor para ser utilizado para la traducción de C escrito prototipo a una pitón llamada de objeto:

cdef double callback(void *parameter, void *method): 
    return (<object>method)(<object>parameter) 

Ahora incrustemos estas características en una clase de Cython:

cdef class PyLabCallBack: 
    cdef ALabCallBack* thisptr 

    def __cinit__(self, method): 
     # 'callback' :: The pattern/converter method to fire a Python 
     #    object method from C typed infos 
     # 'method' :: The effective method passed by the Python user 
     self.thisptr = new ALabCallBack(callback, <void*>method) 

    def __dealloc__(self): 
     if self.thisptr: 
      del self.thisptr 

    cpdef double execute(self, parameter): 
     # 'parameter' :: The parameter to be passed to the 'method' 
     return self.thisptr.cy_execute(<void*>parameter) 

Editar: Mejor escribiendo para ejecutar la función: def ejecutar => cpdef doble

Eso es todo. Llámelo como hacer algo así:

def func(obj): 
    print obj 
    obj.Test()  # Call to a specific method from class 'PyLabNode' 
    return obj.d_prop 

n = PyLabNode() # Custom class of my own 
cb = PyLabCallBack(func) 
print cb.execute(n) 

como Python se escribe de manera implícita, se puede acceder a las propiedades del objeto 'obj' relacionado con la clase del objeto pasado como parámetro cuando llegue el momento de encender la devolución de llamada .

Se puede adaptar bastante fácilmente para la implementación de C puro. Por favor, dígame si puede ver alguna mejora posible para esto (en mi caso, las perforaciones son muy cruciales ya que los eventos se disparan intensamente).

Cuestiones relacionadas