Как я могу заставить методы работать как обратные вызовы с python ctypes?
У меня есть C api, что я взаимодействую с пакетом ctypes python. Все работает хорошо, кроме этого небольшого лакомого кусочка.
Чтобы зарегистрировать функции как обратные вызовы для некоторых уведомлений, я вызываю эту функцию:
void RegisterNotifyCallback( int loginId, int extraFlags, void *(*callbackFunc)(Notification *))
поэтому в python мой код выглядит следующим образом:
CALLBACK = ctypes.CFUNCTYPE(None, ctypes.POINTER(Notification))
func = CALLBACK(myPythonCallback)
myLib.RegisterNofityCallback(45454, 0, func)
Если значение myPythonCallback является FunctionType (т.е. не методом), оно отлично работает и вызывает функцию обратного вызова с правильными параметрами. Если это MethodType, он по-прежнему вызывает этот метод, но затем взрывается с segfault после завершения метода.
изменить
Чтобы уточнить, когда я говорю, что тип функции я означает, что myPythonCallback определяется как функция,
def myPythonCallback(Notification):
...do something with the Notification object i get passed in
Когда я говорю это MethodType, я имею в виду метод класса:
class MyClass(object):
def myPythonCallback(self, Notification):
...do something with Notification
Взорвается второй тип.
Легко ли это? Или может кто-нибудь предложить мне объяснение, почему это происходит?
Большое спасибо,
Ал.
изменить
Копаем немного больше с помощью GDB, это дает мне следующее:
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
и значение, переданное в instancemethod_dealloc, равно:
(gdb) x 0x558ed644 0x558ed644: 0x00000000
Считаете ли вы, что это возможно?
Ответы
Ответ 1
Вы не можете, насколько мне известно, вызвать связанный метод, поскольку он не имеет параметра self. Я решаю эту проблему, используя закрытие, например:
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())
Ответ 2
Это может быть ошибка ctypes, связанная с магией за настройкой стека для методов с функциями.
Вы пытались использовать обертку с помощью лямбда или функции?
Lambda:
func = CALLBACK(lambda x: myPythonCallback(x))
Функции:
def wrapper(x): myPythonCallback(x)
func = CALLBACK(wrapper)
Существует третий метод, использующий закрытие. Если вы настраиваете обратный вызов внутри класса, метод, определенный ниже, должен наследовать аргумент "self" из-за области действия - заставить его действовать как метод класса, несмотря на регулярную функцию.
class Foo:
def __init__(self):
def myPythonCallback(Notification):
print self # it there!
pass # do something with notification
func = CALLBACK(myPythonCallback)
myLib.RegisterNofityCallback(45454, 0, func)