[[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

本WebサイトはcookieをPHPのセッション識別および左欄目次の開閉状況記憶のために使用しています。同意できる方のみご覧ください。More information about cookies
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