我如何获得方法作为python ctypes的回调工作?
我有一个C API,我与python ctypes包接口。一切运作良好,除了这个小珍闻。我如何获得方法作为python ctypes的回调工作?
将函数注册为回调一些通知,我调用这个函数:
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的值是一个函数类型(即不是方法),它工作正常并使用正确的参数调用回调函数。如果它是一个MethodType,它仍然调用该方法,但是一旦该方法完成,就会抛出一个段错误。
编辑
为了澄清,当我说功能型我的意思是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
依你看,这是可以解决的?
这可能是一个ctypes错误,涉及为方法vs函数设置堆栈背后的魔法。
您是否尝试过使用通过lambda或函数的包装?
LAMBDA:
func = CALLBACK(lambda x: myPythonCallback(x))
功能:
def wrapper(x): myPythonCallback(x)
func = CALLBACK(wrapper)
有使用闭包的第三种方法。如果你从类内部设置回调,下面定义的方法应该继承“范围”的“自我”参数 - 尽管它是一个常规函数,它仍然像类方法一样工作。
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)
嗯,我会试试看,代码是在工作,我会尝试在家中复制与您的修复。 – AlMcLean
另一件事 - 你的方法和功能是否有完全相同的动作?有可能在函数期间操作数据以导致段错误一旦返回 – lunixbochs
嗨,是的,它是同样的事情。我正在操纵一个结构,所以我有我的公平份额的段错误,同时挑逗一个,所以我知道要注意什么。 – AlMcLean
就我所知,你不能调用一个绑定方法,因为它缺少自我参数。我使用闭包来解决这个问题,像这样:
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())
好吧,我看到背后的推理。我认为,如果我想从另一个类传递一个绑定方法作为回调函数,我需要做关闭技巧?那么这是一个错误还是它设计的方式? – AlMcLean
注意将self.getCallbackFunc()分配给一个内部变量(例如self.callback),否则func()将被垃圾回收,程序将以SIGSEGV结束。 –
你在C API中使用cdecl或stdcall吗?我不清楚函数类型和方法类型之间的区别是什么意思。请你可以解释更多,可能与代码示例的工作形式和形式没有。顺便说一句,我成功地创建了ctypes的回调,所以我相信这是可能的。 –
请参阅编辑。 – AlMcLean