构造可从C调用的python函数,输入参数具有*output*语义

2024-06-16 12:01:04 发布

您现在位置:Python中文网/ 问答频道 /正文

用例如下:

  • 给定一个用C实现的(固定的,不可更改的)DLL
  • 想要:用python实现的这个DLL的包装器(选择的方法:ctypes)

DLL中的一些函数需要同步原语。为了获得最大的灵活性,DLL的设计者完全依赖于客户端提供的回调。更确切地说,该DLL应具有:

  • 用于创建synchronization对象的回调函数
  • 获取/释放synchronization对象的锁的回调函数
  • 以及一个回调函数来销毁synchronization对象

因为从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

我哪里错了?在


Tags: 对象函数pytestlibcreatecallbackctypes
2条回答

segfault是由于Python回调中错误的指针处理造成的。指针间接寻址的级别比严格的必需级别多,这可能是造成混淆的原因。在Python回调中设置p_p_i.contents,但这只会更改Python ctypes对象指向的内容,而不是底层指针。为此,可以通过数组访问语法来进行指针反引用。一个经过提炼的例子:

ip = ctypes.POINTER(ctypes.c_int)()
i = ctypes.c_int(99)
# Wrong way
ipp = ctypes.pointer(ip)
ipp.contents = ctypes.pointer(i)
print bool(ip) # False  > still NULL
# Right way
ipp = ctypes.pointer(ip)
ipp[0] = ctypes.pointer(i)
print ip[0] # 99  > success!

类型错误是由peterhansen的答案中描述的类型不兼容造成的。在

您似乎没有正确定义返回类型。看起来您的C回调返回一个int,而Python回调则声明为return C_int,但没有显式返回任何内容(因此实际上返回None)。如果你“返回0”,它可能会停止崩溃。您应该这样做,或者在任何情况下将回调签名更改为CFUNCTYPE(None, ...etc)。在

另外,尽管这不是当前的问题,但您在跟踪“int”内置名称。这可能会导致以后出现问题。在

编辑:将C返回类型正确地称为“int”,而不是“void”。在

相关问题 更多 >