如何使用python ctypes获取方法作为回调?

AlM*_*ean 12 python ctypes

我有一个C api,我正在与python ctypes包接口.一切都很好,除了这个小小的花絮.

要将函数注册为某些通知的回调,我调用此函数:

void RegisterNotifyCallback( int loginId, int extraFlags, void *(*callbackFunc)(Notification *))
Run Code Online (Sandbox Code Playgroud)

所以在python我的代码看起来像:

CALLBACK = ctypes.CFUNCTYPE(None, ctypes.POINTER(Notification))
func = CALLBACK(myPythonCallback)
myLib.RegisterNofityCallback(45454, 0, func)
Run Code Online (Sandbox Code Playgroud)

如果myPythonCallback的值是FunctionType(即不是方法),它可以正常工作并使用正确的参数调用回调函数.如果它是一个MethodType,它仍会调用方法,但一旦方法完成,就会出现段错误.

编辑

澄清一下,当我说函数类型时,我的意思是将myPythonCallback定义为函数,

def myPythonCallback(Notification):
    ...do something with the Notification object i get passed in
Run Code Online (Sandbox Code Playgroud)

当我说它是一个MethodType时,我的意思是一个类方法:

class MyClass(object):
    def myPythonCallback(self, Notification):
        ...do something with Notification
Run Code Online (Sandbox Code Playgroud)

它爆炸了第二种类型.

这周围有一个轻松的地方吗?或者有人可以向我解释为什么会发生这种情况?

非常感谢,Al.

编辑

用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
Run Code Online (Sandbox Code Playgroud)

传递给instancemethod_dealloc的值是:

(gdb) x 0x558ed644 0x558ed644:     0x00000000
Run Code Online (Sandbox Code Playgroud)

你认为这是可以解决的吗?

Dav*_*nan 9

据我所知,你不能调用绑定方法,因为它缺少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())
Run Code Online (Sandbox Code Playgroud)

  • 小心地将self.getCallbackFunc()分配给内部变量(例如self.callback),否则func()将被垃圾回收,程序将以SIGSEGV结尾。 (3认同)

lun*_*chs 4

这可能是一个 ctypes 错误,涉及为方法与函数设置堆栈背后的魔力。

您是否尝试过通过 lambda 或函数使用包装器?

拉姆达:

func = CALLBACK(lambda x: myPythonCallback(x))
Run Code Online (Sandbox Code Playgroud)

功能:

def wrapper(x): myPythonCallback(x)
func = CALLBACK(wrapper)
Run Code Online (Sandbox Code Playgroud)

还有第三种方法使用闭包。如果您从类内部设置回调,则由于作用域,下面定义的方法应该继承“self”参数 - 使其表现得像类方法,尽管它是常规函数。

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)
Run Code Online (Sandbox Code Playgroud)