1

用例如下:

  • 给定一个用 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

我哪里错了?

4

2 回答 2

0

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

此外,虽然这不是当前的问题,但您正在隐藏“int”内置名称。这可能会导致以后出现问题。

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

于 2009-12-12T13:50:59.083 回答
0

段错误是由于 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!

类型错误是由于彼得汉森的回答中描述的类型不兼容。

于 2010-09-09T02:19:17.290 回答