我如何获得方法作为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 

依你看,这是可以解决的?

+0

你在C API中使用cdecl或stdcall吗?我不清楚函数类型和方法类型之间的区别是什么意思。请你可以解释更多,可能与代码示例的工作形式和形式没有。顺便说一句,我成功地创建了ctypes的回调,所以我相信这是可能的。 –

+0

请参阅编辑。 – AlMcLean

这可能是一个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) 
+0

嗯,我会试试看,代码是在工作,我会尝试在家中复制与您的修复。 – AlMcLean

+0

另一件事 - 你的方法和功能是否有完全相同的动作?有可能在函数期间操作数据以导致段错误一旦返回 – lunixbochs

+0

嗨,是的,它是同样的事情。我正在操纵一个结构,所以我有我的公平份额的段错误,同时挑逗一个,所以我知道要注意什么。 – 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()) 
+0

好吧,我看到背后的推理。我认为,如果我想从另一个类传递一个绑定方法作为回调函数,我需要做关闭技巧?那么这是一个错误还是它设计的方式? – AlMcLean

+1

注意将self.getCallbackFunc()分配给一个内部变量(例如self.callback),否则func()将被垃圾回收,程序将以SIGSEGV结束。 –