ctypesのコールバック関数でユーザデータとしてPythonオブジェクト
質問
C関数myfunc
は、データのより大きなチャンクで動作します。結果はコールバック関数にチャンクで返されます:
int myfunc(const char *data, int (*callback)(char *result, void *userdata), void *userdata);
のctypesのを使用して、Pythonコードからmyfunc
を呼び出すと、結果はPythonのコールバック関数に返されて持って大したません。このコールバックは正常に動作します。
myfunc = mylib.myfunc
myfunc.restype = c_int
myfuncFUNCTYPE = CFUNCTYPE(STRING, c_void_p)
myfunc.argtypes = [POINTER(c_char), callbackFUNCTYPE, c_void_p]
def mycb(result, userdata):
print result
return True
input="A large chunk of data."
myfunc(input, myfuncFUNCTYPE(mycb), 0)
しかし、コールバック関数へのユーザデータとしてPythonオブジェクト(例えばリスト)を与える方法はありますか?結果チャンクを離れて格納するためには、私は、例えばをしたいと思います:ます。
def mycb(result, userdata):
userdata.append(result)
userdata=[]
しかし、それはmyfunc関数の呼び出しで使用できるように、私は、c_void_pにPythonのリストをキャストする方法は考えている。
私の現在の回避策は、かなり面倒ですctypesの構造としてリンクリストを実装することです。
解決
私は多分...あなたはそれを行うためにのPythonのC API を使うことができると思いますあなたはPyObjectポインタを使用することができます。
編集:opがコメントで指摘したように、そこのctypesで容易に入手可能py_object
タイプはすでにですので、解決策は、Pythonのリストから最初のctypes.py_object
オブジェクトを作成し、その後にそれをキャストすることですC関数の引数として渡しするc_void_p
は(私は、このステップは任意のポインタを受け入れるべきvoid*
として入力パラメータとして不要かもしれないと思うと、それだけでbyref
を渡すために速くなります)。コールバックでは、逆の手順が行われる(py_object
へのポインタを無効ポインタからキャストした後、内容の値を取得する)。
この問題を回避するには...それはすでにそれがアイテムを追加するために持っているリストに知っているように、コールバック関数にクロージャを使用することができます。
myfunc = mylib.myfunc
myfunc.restype = c_int
myfuncFUNCTYPE = CFUNCTYPE(STRING)
myfunc.argtypes = [POINTER(c_char), callbackFUNCTYPE]
def mycb(result, userdata):
userdata.append(result)
input="A large chunk of data."
userdata = []
myfunc(input, myfuncFUNCTYPE(lambda x: mycb(x, userdata)))