2011-08-31 10 views
11

Tengo una C api que estoy interactuando con el paquete python ctypes. Todo funciona bien, excepto este pequeño bocado.¿Cómo puedo hacer que los métodos funcionen como devoluciones de llamada con los tipos de python?

para registrar funciones como las devoluciones de llamada a algunas notificaciones, i llamar a esta función:

void RegisterNotifyCallback(int loginId, int extraFlags, void *(*callbackFunc)(Notification *)) 

por lo que en pitón mi código es el siguiente:

CALLBACK = ctypes.CFUNCTYPE(None, ctypes.POINTER(Notification)) 
func = CALLBACK(myPythonCallback) 
myLib.RegisterNofityCallback(45454, 0, func) 

Si el valor de myPythonCallback es un FunctionType (es decir, no es un método), funciona bien y llama a la función de devolución de llamada con los parámetros correctos. Si es un MethodType, aún llama al método, pero luego explota con una segfault una vez que el método ha finalizado.

edición

para aclarar, cuando digo el tipo de función que quiero decir que myPythonCallback se define como una función,

def myPythonCallback(Notification): 
    ...do something with the Notification object i get passed in 

cuando digo que es un tipoMetodo, me refiero a un método de clase:

class MyClass(object): 
    def myPythonCallback(self, Notification): 
     ...do something with Notification 

Se explota en el segundo tipo.

¿Hay algo fácil alrededor de esto? ¿O alguien puede ofrecerme una explicación de por qué está sucediendo esto?

Muchas gracias, Al.

edición

Excavar un poco más con el BGF me da esto:

Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 1544567712 (LWP 5389)] 0x555ae962 in instancemethod_dealloc (im=0x558ed644) at Objects/classobject.c:2360 2360 Objects/classobject.c: No such file or directory. 
     in Objects/classobject.c (gdb) backtrace 
#0 0x555ae962 in instancemethod_dealloc (im=0x558ed644) at Objects/classobject.c:2360 
#1 0x555f6a61 in tupledealloc (op=0x5592010c) at Objects/tupleobject.c:220 
#2 0x555aeed1 in instancemethod_call (func=0x558db8ec, arg=0x5592010c, kw=0x0) at Objects/classobject.c:2579 
#3 0x5559aedb in PyObject_Call (func=0x558db8ec, arg=0x5592c0ec, kw=0x0) at Objects/abstract.c:2529 
#4 0x5563d5af in PyEval_CallObjectWithKeywords (func=0x558db8ec, arg=0x5592c0ec, kw=0x0) at Python/ceval.c:3881 
#5 0x5559ae6a in PyObject_CallObject (o=0x0, a=0x0) at Objects/abstract.c:2517 

y el valor pasado a instancemethod_dealloc es:

(gdb) x 0x558ed644 0x558ed644:  0x00000000 

Qué te parece este se puede arreglar?

+0

¿Estás usando cdecl o stdcall en la API C? No sé a qué te refieres con la distinción entre tipo de función y tipo de método. Podría explicar más, posiblemente con muestras de código del formulario que funciona y el formulario que no. Por cierto, he creado devoluciones de llamadas con ctypes, así que estoy seguro de que es posible. –

+0

Por favor, vea la edición. – AlMcLean

Respuesta

3

Esto puede ser un error de ctypes que trata de la magia detrás de la configuración de la pila para métodos vs funciones.

¿Ha intentado utilizar un envoltorio mediante lambda o función?

Lambda:

func = CALLBACK(lambda x: myPythonCallback(x)) 

Función:

def wrapper(x): myPythonCallback(x) 
func = CALLBACK(wrapper) 

Hay un tercer método mediante cierres. Si está configurando la devolución de llamada desde dentro de la clase, el método definido a continuación debe heredar el argumento "self" debido al alcance, lo que lo hace actuar como un método de clase a pesar de ser una función normal.

class Foo: 
    def __init__(self): 
     def myPythonCallback(Notification): 
      print self # it's there! 
      pass # do something with notification 

     func = CALLBACK(myPythonCallback) 
     myLib.RegisterNofityCallback(45454, 0, func) 
+0

Hmm, lo intentaré, el código está en funcionamiento, intentaré replicarlo en casa con sus correcciones. – AlMcLean

+0

otra cosa: ¿su método y función tienen exactamente la misma acción? es posible manipular datos durante la función para causar un segfault una vez devuelto – lunixbochs

+0

Hola, sí, es lo mismo. Estoy manipulando una estructura así que he tenido mi parte justa de segfeults mientras me burlo de eso, así sé lo que debo vigilar. – AlMcLean

7

Por lo que sé, no puede llamar a un método enlazado porque le falta el parámetro automático.Resuelvo este problema usando un cierre, como este:

CALLBACK = ctypes.CFUNCTYPE(None, ctypes.POINTER(Notification)) 

class MyClass(object): 

    def getCallbackFunc(self): 
     def func(Notification): 
      self.doSomething(Notification) 
     return CALLBACK(func) 

    def doRegister(self): 
     myLib.RegisterNofityCallback(45454, 0, self.getCallbackFunc()) 
+0

Ok, veo el razonamiento detrás de eso. Supongo que si quiero pasar un método vinculado de otra clase ya que la devolución de llamada necesito hacer el truco de cierre. Entonces, ¿esto es un error o simplemente la forma en que está diseñado? – AlMcLean

+1

Tenga cuidado de asignar self.getCallbackFunc() a una variable interna (por ejemplo, self.callback), de lo contrario, func() se recolectará y el programa terminará con SIGSEGV. –

Cuestiones relacionadas