Как я могу заставить методы работать как обратные вызовы с 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)