ctypesはPythonから動的ライブラリを呼ぶための標準ライブラリ。最近初めて使った際に得た教訓/ハマったポイントをメモしておく。
restypeとargtypesは必ず指定する
動的ライブラリの関数を呼び出す時には、Cと互換性のある型を指定する必要がある。
(ref.) http://docs.python.jp/2/library/ctypes.html#ctypes-fundamental-data-types
この型の指定は、関数呼び出し時に指定するか、restype
/argtypes
により戻り値/引数の型を呼び出し前にあらかじめ指定しておくか、のどちらかにより行う。
from ctypes import *
# 実行時に指定
ret = test.func2(c_char_p("test"), c_char_p("test2"), c_char_p("test3"))
# restype/argtypesにより指定
test.func2.restype = c_char_p
test.func2.argtypes = (c_char_p, c_char_p, c_char_p)
ret = test.func2("test", "test2", "test3")
実行時に指定するのは呼び出しが多くなると面倒になってくるため、ライブラリのインターフェースを定義したモジュールを作るのが良いと考えている。Cのヘッダファイルと同じ感じか。
from ctypes import *
test.func1.restype = c_int
test.func2.argtypes = (None)
test.func2.restype = c_char_p
test.func2.argtypes = (c_char_p, c_char_p, c_char_p)
構造体のポインタ型の指定
構造体はPythonの世界ではctypes.Structure
を継承したクラスで表す。
構造体のメンバは、__fields__
フィールドにタプルの配列で指定する。タプルは(<フィールド名>, <型>)
という形式でなければならない。
from ctypes import *
class JSON_T(Structure):
__fields__ = [('type', c_int), ('refcount', c_size_t)]
この構造体のポインタ型を指定したい場合はctypes.POINTER
関数を使う。
(ref.)http://docs.python.jp/2/library/ctypes.html#ctypes.POINTER
from ctypes import *
class JSON_T(Structure):
__fields__ = [('type', c_int), ('refcount', c_size_t)]
test.func3.restype = c_char_p
test.func3.argtypes = (c_void_p, c_char_p, c_char_p, c_char_p, POINTER(JSON_T))
ポインタのポインタ型の指定
ctypes.c_char_p
やctypes.c_void_p
など元々ポインタを表している型については、ctypes.POINTER
関数を使えばよい。
from ctypes import *
test.func4.restype = c_char_p
test.func4.argtypes = (c_void_p, c_char_p, c_char_p, c_char_p, POINTER(c_void_p), POINTER(c_char_p))
これらを使用する際には、ctypes.c_char_p
/ctypes.c_void_p
で指定した変数を、ctypes.byref
関数に与えて参照を取得する必要がある。
from ctypes import *
test.func4.restype = c_char_p
test.func4.argtypes = (c_void_p, c_char_p, c_char_p, c_char_p, POINTER(c_void_p), POINTER(c_char_p))
# arg1は既に定義済みとする
arg2 = 'str2'
arg3 = 'str3'
arg4 = 'str4'
void_p_p = c_void_p(None)
char_p_p = c_char_p(None)
# 以下だとポインタのポインタにNULLが代入されるためダメ
# void_p_p = None
# char_p_p = None
test.func4(arg1, arg2, arg3, arg4, byref(void_p_p), byref(char_p_p))
(注) ctypes.c_int
などプリミティブ型を表している型については今回使用していないので分からない。ctypes.POINTER
関数を二重に使うのか?