用例如下:
- 给定一个用 C 实现的(固定的,不可更改的)DLL
- 需要:在 python 中实现的这个 DLL 的包装器(选择的方法:ctypes)
DLL 中的一些函数需要同步原语。为了获得最大的灵活性,DLL 的设计者完全依赖于客户端提供的回调。更准确地说,该 DLL 应具有:
- 用于创建同步对象的回调函数
- 获取/释放同步对象锁定的回调函数
- 和一个用于销毁同步对象的回调函数
因为从DLL的角度来看,同步对象是不透明的,它会被一个void *
实体所代表。例如,如果其中一个 DLL 函数想要获取锁,它应该这样做:
void* mutex;
/* get the mutex object via the create_mutex callback */
create_mutex(&mutex);
/* acquire a lock */
lock_mutex(mutex);
... etc
可以看出,回调create_mutex
输入参数具有输出语义。这是通过void **
签名实现的。
这个回调(和其他三个)必须在 python 中实现。我失败了 :-) 为简单起见,让我们只关注创建回调,也为简单起见,让不透明对象为int
.
模拟回调使用的玩具 DLL 如下 (ct_test.c):
#include <stdio.h>
#include <stdlib.h>
typedef int (* callback_t)(int**);
callback_t func;
int* global_i_p = NULL;
int mock_callback(int** ipp)
{
int* dynamic_int_p = (int *) malloc(sizeof(int));
/* dynamic int value from C */
*dynamic_int_p = 2;
*ipp = dynamic_int_p;
return 0;
}
void set_py_callback(callback_t f)
{
func = f;
}
void set_c_callback()
{
func = mock_callback;
}
void test_callback(void)
{
printf("global_i_p before: %p\n", global_i_p);
func(&global_i_p);
printf("global_i_p after: %p, pointed value:%d\n", global_i_p, *global_i_p);
/* to be nice */
if (func == mock_callback)
free(global_i_p);
}
想要提供回调并使用 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
我哪里错了?