用例如下:
DLL中的一些函数需要同步原语。为了获得最大的灵活性,DLL的设计者完全依赖于客户端提供的回调。更确切地说,该DLL应具有:
因为从DLL的角度来看,synchronization对象是不透明的,它将由void *
实体表示。例如,如果某个DLL函数想要获得锁,它应该:
void* mutex;
/* get the mutex object via the create_mutex callback */
create_mutex(&mutex);
/* acquire a lock */
lock_mutex(mutex);
... etc
可以看出,callback create_mutex
输入参数具有输出语义。这是通过void **
签名实现的。在
这个回调(以及其他三个)必须用python实现。我失败了:-)为了简单起见,我们只关注创建回调,为了简单起见,让不透明对象成为int
。在
模拟回调使用的toy DLL如下所示(ct_test.c):
^{pr2}$提供回调并使用DLL的python代码如下:
from ctypes import *
lib = CDLL("ct_test.so")
# "dynamic" int value from python
int = c_int(1)
int_p = pointer(int)
def pyfunc(p_p_i):
p_p_i.contents = int_p
# create callback type and instance
CALLBACK = CFUNCTYPE(c_int, POINTER (POINTER(c_int)))
c_pyfunc = CALLBACK(pyfunc)
# functions from .so
set_py_callback = lib.set_py_callback
set_c_callback = lib.set_c_callback
test_callback = lib.test_callback
# set one of the callbacks
set_py_callback(c_pyfunc)
#set_c_callback()
# test it
test_callback()
当使用in-DLL提供的回调(通过set_c_callback()
设置)时,这将按预期工作:
~/dev/test$ python ct_test.py
global_i_p before: (nil)
global_i_p after: 0x97eb008, pointed value:2
但是,在另一种情况下(使用python回调)失败:
~/dev/test$ python ct_test.py
global_i_p before: (nil)
Traceback (most recent call last):
File "/home/packages/python/2.5/python2.5-2.5.2/Modules/_ctypes/callbacks.c", line 284, in 'converting callback result'
TypeError: an integer is required
Exception in <function pyfunc at 0xa14079c> ignored
Segmentation fault
我哪里错了?在
segfault是由于Python回调中错误的指针处理造成的。指针间接寻址的级别比严格的必需级别多,这可能是造成混淆的原因。在Python回调中设置
p_p_i.contents
,但这只会更改Python ctypes对象指向的内容,而不是底层指针。为此,可以通过数组访问语法来进行指针反引用。一个经过提炼的例子:类型错误是由peterhansen的答案中描述的类型不兼容造成的。在
您似乎没有正确定义返回类型。看起来您的C回调返回一个int,而Python回调则声明为return C_int,但没有显式返回任何内容(因此实际上返回None)。如果你“返回0”,它可能会停止崩溃。您应该这样做,或者在任何情况下将回调签名更改为
CFUNCTYPE(None, ...etc)
。在另外,尽管这不是当前的问题,但您在跟踪“int”内置名称。这可能会导致以后出现问题。在
编辑:将C返回类型正确地称为“int”,而不是“void”。在
相关问题 更多 >
编程相关推荐