2011-08-31 9 views
11

Mam api C, że jestem interfejs z pakietem python ctypes. Wszystko działa dobrze, z wyjątkiem tego małego smakołyku.Jak mogę uzyskać metody do pracy jako wywołania zwrotne w python ctypes?

Aby zarejestrować funkcje jak callbacków niektórych powiadomień, wzywam tę funkcję:

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

tak w python mój kod wygląda następująco:

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

Jeżeli wartość myPythonCallback jest FunctionType (tj nie jest to metoda), działa dobrze i wywołuje funkcję zwrotną z odpowiednimi parametrami. Jeśli jest to MethodType, nadal wywołuje metodę, ale następnie wysadza się z segfault po zakończeniu metody.

edit

celu wyjaśnienia, kiedy mówię typ funkcji i oznacza to, że myPythonCallback jest zdefiniowana jako funkcja,

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

kiedy mówię, że to MethodType, to znaczy metoda klasy:

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

Wysypuje się na drugi typ.

Czy jest to łatwe? A może ktoś zaoferuje mi wyjaśnienie, dlaczego tak się dzieje?

Wielkie dzięki, Al.

edit

Kopanie trochę dalej z GDB daje mi to:

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 

i wartość przekazana do instancemethod_dealloc jest:

(gdb) x 0x558ed644 0x558ed644:  0x00000000 

Czy myślisz to naprawić?

+0

Czy używasz CDecl lub stdcall w C API? Nie wiem, co masz na myśli przez rozróżnienie między typem funkcji a typem metody. Czy mógłbyś wyjaśnić więcej, prawdopodobnie przy pomocy próbek kodu formularza, który działa, a formularza, który nie działa. Nawiasem mówiąc, udało mi się stworzyć wywołania zwrotne z ctypes, więc jestem pewien, że to jest możliwe. –

+0

Proszę zobaczyć edycję. – AlMcLean

Odpowiedz

3

Może to być błąd typu ctypes dotyczący magii za ustawieniem stosu dla metod i funkcji.

Czy próbowałeś używać opakowania przez lambdę lub funkcję?

Lambda:

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

Funkcja:

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

Jest trzecia metoda za pomocą zamknięć. Jeśli konfigurujesz wywołanie zwrotne z klasy, metoda zdefiniowana poniżej powinna dziedziczyć argument "self" ze względu na zakres - co sprawia, że ​​działa on jak metoda klasy, mimo że jest to zwykła funkcja.

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, spróbuję, kod będzie działał, spróbuję i zreplikować w domu twoje poprawki. – AlMcLean

+0

jeszcze jedno - czy twoja metoda i funkcja mają dokładnie to samo działanie? możliwe jest manipulowanie danymi podczas funkcji, aby spowodować segfault po zwrocie. – lunixbochs

+0

Cześć, tak to jest to samo. Manipuluję strukturą, więc miałem sporo segfaultów, jednocześnie drażniąc to, więc wiem, na co uważać. – AlMcLean

7

Nie można, o ile wiem, wywołać metody związanej, ponieważ brakuje jej parametru własnego.Rozwiązuję ten problem za pomocą zamknięcia, na przykład:

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, widzę uzasadnienie tego. Rozumiem, że jeśli chcę przekazać związaną metodę z innej klasy jako callback, muszę zrobić sztuczkę zamknięcia? Czy to błąd, czy sposób, w jaki został zaprojektowany? – AlMcLean

+1

Należy ustawić właściwość self.getCallbackFunc() na wewnętrzną zmienną (np. Self.callback), w przeciwnym razie func() zostanie zbuforowane, a program zakończy się SIGSEGV. –

Powiązane problemy