PythonでのFFI
Pythonでは、FFI(Foreign Function Interface)としてctypesモジュールを使います
DLL
読み込むDLLのCソース dll.c:
// gcc -fPIC -shared -o dll.so dll.c typedef struct { int age; } Foo; Foo newFoo(int age) { Foo f; f.age = age; return f; } int getAge(Foo* foo) { return foo->age; } void callFoo(Foo* foo, void (*func)(Foo*)) { func(foo); } typedef Foo NewFunc(int); NewFunc* getNew() { return &newFoo; }
(とりあえず、いろいろやってます)。これをdll.soとしてスクリプトと同じディレクトリに置いてます。
余談ですけど、以下のと最後の関数は同じ(→C言語での複雑な型表記を理解する方法 - ラシウラ)ですが、自重してあります:
Foo (*getNew())(int) { return &newFoo; }
Python
dll.soにアクセスするスクリプト
# load dll import os import ctypes this_file = os.path.abspath(__file__) dllfile = os.path.join(os.path.dirname(this_file), "dll.so") dll = ctypes.CDLL(dllfile) # struct # typedef struct { int age; } Foo class Foo(ctypes.Structure): _fields_ = [("age", ctypes.c_int)] # call func # Foo newFoo(int age) # dll.newFoo.argtypes = [ctypes.c_int] # standard type is not required dll.newFoo.restype = Foo foo = dll.newFoo(10) print foo.age # pointer # int getAge(Foo* foo) dll.getAge.argtypes = [ctypes.POINTER(Foo)] dll.getAge.restype = ctypes.c_int # obj to pointer print dll.getAge(ctypes.pointer(foo)) # func type # void (*callback)(Foo*) callbackType = ctypes.CFUNCTYPE(None, ctypes.POINTER(Foo)) # void callFoo(Foo* foo, void (*callback)(Foo*)) dll.callFoo.argtypes = [ctypes.POINTER(Foo), callbackType] # callbacked func def callback(pFoo): # pointer to obj foo = pFoo.contents print "foo.age: %d" % foo.age pass # func obj dll.callFoo(ctypes.pointer(foo), callbackType(callback)) # use returned c function # typedef Foo (*PNewFunc)(int); newfuncType = ctypes.CFUNCTYPE(Foo, ctypes.c_int) # PNewFunc getNew() dll.getNew.restype = newfuncType newfunc = dll.getNew() print newfunc print newfunc.restype bar = newfunc(20) print bar.age
基本は、dllをロードして、使うCの関数にargtypesとrestypeをセットして呼び出すという感じです。標準でない型やポインタや関数は作ってやる必要があります。関数型はポインタ型になってます。
その他
野良DLLを読み込ませるには、上記のように絶対PATHを指定するか、LD_LIBRARY_PATHをpythonに渡すかする必要があります。
インストールされたDLLはファイル名を見つけるユーティリティがあるようです。
import ctypes.util mfile = ctypes.util.find_library("m")
"lib"のあるなし、".so"や".dylib"、".dll"などの拡張子を補完するようです。
触れわすれたけど、配列は、型から*で作れます。
int5array = ctypes.c_int * 5 arr = int5array(1, 2, 3, 4, 5) print arr[3] # => 4