[[ctypes]]

ctypes

PythonとCの連携方法

PythonからCのコードを使うには、複数の方法がある。

いろいろあるが、ctypesとBoost.Pythonを取り上げると、

Boost.Pythonctypes
形態C++ライブラリPython標準ライブラリ
PythonからCを利用
CからPythonを利用×
Cのメモリ管理(GC)面倒見てくれる見てくれない?
Cの記述・コンパイル必要自作するなら必要

Cコードを触る・コンパイルすることを厭わなければ、Boost.Pythonの方が汎用性が高い。

既存のライブラリを変更せず利用するだけならPythonだけで完結するためctypesの方が手軽……

とはいえC側での型定義などを調べる必要はあるし、複雑なクラスはほぼそのまま構造をPythonで再定義してやらなければならないので、あくまで簡単な処理を呼び出すのが現実的。

ctypes利用方法

あくまで動的ライブラリ(Windowsでは.dll, Linuxでは.so)を読み込むもので、静的ライブラリ(.lib)は読み込めない。

# 唐突なNim

proc add(x, y: cdouble): cdouble {.exportc, dynlib.} = 
    return x + y

> nim c -d:release --app:lib sample.nim

これで、add()という関数を持ったsample.dllができる。exportc, dynlib あたりがdll化に必要らしい。

で、これをPythonから利用する。

単純には使用できず、型を教えてやる必要がある。

Pythonでの型のメモリ構造を、Cでの型のメモリ構造に互換性のある形で渡さなければいけないが、.dll自体は関数の引数が何の型かなんて情報は持っていない。それをctypesを通して教えることで、Pythonの方で正しく変換してから渡すようになる。これを指定しないと、整数を渡したら浮動小数で解釈され、てんで違った結果が帰ってきたりする。

import ctypes as C

lib = C.cdll.LoadLibrary('sample.dll')
lib.add.argtypes = [C.c_double, C.c_double]  # 引数は2つ、double型ですよー
lib.add.restype = C.c_double                 # 返値はdouble型ですよー

res = lib.add(3, 8)  # int型整数を渡しても
print(res)           # => 11.0  doubleでちゃんと解釈されて返ってくる

NumPy

numpy.ndarrayも渡せると便利。NumPyモジュールに、NumPyの型をCと連携させるための指定方法が準備されている。

ただ、1次元配列をCの関数に渡すのはできたが、2次元配列になったり、返値として受け取る方法がどうにもよくわからず。ポインタムヅカシイ。

ひとまず渡す方をメモ。

# 1次元配列を合計して返すだけ
# NumPy配列はopenArray[]で受け取る

proc arraySum(xxx: openArray[float]): float {.exportc, dynlib.} =
    return xxx.sum

openArrayは、Cにコンパイルされるとき、実は引数が追加されている。

nimcacheフォルダにできるsample2.cを見ると、

// ...
N_LIB_EXPORT N_CDECL(NF, arraySum)(NF* xxx, NI xxxLen_0) {  // ←ここ
	NF result;
{	result = (NF)0;
	result = sum_o0A2tEZqgAaJbmC0q9asV1g(xxx, xxxLen_0);
	goto BeforeRet_;
	}BeforeRet_: ;
	return result;
}
// ...

NF* xxxはNimで定義した変数だが、その次にNI xxxLen_0が追加されている。これは、配列の長さを示すもの。

で、ctypesで関数を呼ぶときには、NimではなくこのCに添った引数を与えることになる。

import ctypes as C
import numpy as np

lib = C.cdll.LoadLibrary('sample2.dll')
lib.arraySum.argtypes = [np.ctypeslib.ndpointer(flags='C'), C.c_int]
lib.arraySum.restype = C.c_double

a = np.array([1, 2, 3, 4], dtype=np.float)
res = lib.arraySum(a, len(a))
print(res)  # => 10.0

programming/python/packages/ctypes.txt · 最終更新: 2018/10/05 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0