C++でライブラリー(DLL)を作り、pythonなどから利用する方法について連載します。
1. C++でライブラリーを作る Linux編
2. pythonからC++のライブラリーを利用する Linux編
3. C++でライブラリー(DLL)を作る Windows編
4. pythonからC++のライブラリー(DLL)を利用する Windows編
5. C#からC++のライブラリー(DLL)を利用する
6. python・C#とC++間で複素数を受け渡す
ここではpythonから上記1.にてC++で作成したライブラリーを利用する方法について説明します。
概要
pythonからCで作ったライブラリーを利用するときはctypesを使います。LoadLibrary()でライブラリーのインスタンスを作成します。argtype(またはargtypes)で引数の型を、restypeで戻り値の型を指定してライブラリーのエクスポート関数を実行します。
ctypesにはc_charやc_doubleなどCの主要な変数の型が用意されています。ただしポインターはc_void_pとc_char_pくらいしか見当たりません。ライブラリーの引数や戻り値に他の型のポインターがある場合、とりあえずc_void_pでC側へアドレスを渡してあげれば動いてくれています(本例では配列および構造体のポインターが該当)。
ctypes.StructureでCと同じ構造体を作ることも出来ます。ctypes.byref(構造体のインスタンス)で構造体のポインターを渡すことも出来ます。
pythonの文字列は文字コードを encode('utf-8') を使って変換するとC側でも判読出来ます。逆にCの文字列をpythonで利用するときは decode('utf-8') を利用します。
サンプルコード
import os
import sys
import ctypes
class tagLibParameter(ctypes.Structure):
_fields_ = [("dInA", ctypes.c_double),
("dInB", ctypes.c_double),
("dOut", ctypes.c_double),
("sData", ctypes.c_char * 64)]
def main(argv):
#----------------
global LLib
#----------------
LLib = ctypes.cdll.LoadLibrary("./libLocalLib.so")
#================================================================
nA = 1
nB = 2
#---------------------
nRet = LLibSum(nA, nB)
#---------------------
print('%d + %d = %d' % (nA, nB, nRet))
#================================================================
dsArray = ctypes.c_double * 3
dsIn = dsArray()
dsOut = dsArray()
dsIn[0] = 1.1
dsIn[1] = 2.2
dsIn[2] = 3.3
#--------------------------
LLibCalcX10(dsOut, dsIn, 3)
#--------------------------
print('in[%.1lf %.1lf %.1lf] out[%.1lf %.1lf %.1lf]' % (\
(dsIn[0]), (dsIn[1]), (dsIn[2]),\
(dsOut[0]), (dsOut[1]), (dsOut[2]) ))
#================================================================
sRxBuf = ctypes.create_string_buffer(256)
sTxBase = 'App->DLL: Hello !'
sTxBuf = sTxBase.encode('utf-8')
#-----------------------------------
LLibTxRxMessage(sRxBuf, sTxBuf, 256)
#-----------------------------------
print('%s' % (sRxBuf.value.decode('utf-8')))
#================================================================
LP = tagLibParameter()
(LP.dInA) = 1.1
(LP.dInB) = 2.2
#-----------------------------
LLibInOutStr(ctypes.byref(LP))
#-----------------------------
print('%.1lf + %.1lf = %.1lf [%s]' % (\
(LP.dInA), (LP.dInB), (LP.dOut), (LP.sData.decode('utf-8'))))
def LLibSum(nInA, nInB):
LLib.LLibSum.argtypes = (ctypes.c_int, ctypes.c_int)
LLib.LLibSum.restype = ctypes.c_int
#--------------------------------
nRet = LLib.LLibSum(nInA, nInB)
#--------------------------------
return nRet
def LLibCalcX10(dpOut, dpIn, nInLen):
LLib.LLibCalcX10.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int)
LLib.LLibCalcX10.restype = ctypes.c_int
#--------------------------------
nRet = LLib.LLibCalcX10(dpOut, dpIn, nInLen)
#--------------------------------
return nRet
def LLibTxRxMessage(bpOut, bpIn, nInLen):
LLib.LLibTxRxMessage.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int)
LLib.LLibTxRxMessage.restype = ctypes.c_int
#--------------------------------
nRet = LLib.LLibTxRxMessage(bpOut, bpIn, nInLen)
#--------------------------------
return nRet
def LLibInOutStr(sp):
LLib.LLibInOutStr.argtype = ctypes.c_void_p
LLib.LLibInOutStr.restype = ctypes.c_int
#--------------------------------
nRet = LLib.LLibInOutStr(sp)
#--------------------------------
return nRet
if __name__ == '__main__':
main(sys.argv)
実行例
CallLib.pyとlibLocalLib.soを同じディレクトリーに置いて実行して下さい。pythonとライブラリーの間で、変数・配列・文字列・構造体の双方向のやりとりが出来ています。Cで作った呼び出し側のサンプルコード実行時と同じ結果です。
$ python CallLib.py
start DLL
1 + 2 = 3
in[1.1 2.2 3.3] out[11.0 22.0 33.0]
DLL->App: Hi ! rcvd[App->DLL: Hello !]
1.1 + 2.2 = 3.3 [2025 11/18 19:43:19 331]
end DLL