ユースケースは次のとおりです。
- Cで実装された(固定された、変更不可能な)DLLが与えられた場合
- 求む: Python で実装されたこの DLL のラッパー (選択したメソッド: ctypes)
DLL の一部の関数には、同期プリミティブが必要です。最大限の柔軟性を目指して、DLL の設計者はクライアント提供のコールバックに完全に依存しています。より正確には、この DLL には以下が必要です。
- 同期オブジェクトを作成するためのコールバック関数
- 同期オブジェクトのロックを取得/解放するコールバック関数
- 同期オブジェクトを破棄する 1 つのコールバック関数
DLL の観点からは、同期オブジェクトは不透明であるため、void *
エンティティによって表現されます。たとえば、DLL 関数の 1 つがロックを取得したい場合は、次のようにします。
void* mutex;
/* get the mutex object via the create_mutex callback */
create_mutex(&mutex);
/* acquire a lock */
lock_mutex(mutex);
... etc
コールバックcreate_mutex
入力パラメータには出力セマンティクスがあることがわかります。これはvoid **
署名によって達成されます。
このコールバック (および他の 3 つ) は、Python で実装する必要があります。私は失敗しました :-) 簡単にするために、コールバックの作成のみに焦点を当てましょう。また、簡単にするために、不透明なオブジェクトをint
.
コールバックの使用をエミュレートする toy-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()
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
どこが間違っていますか?