PythonからC++クラスを呼び出す(2)
今回は Python の ctypes モジュールを使い C++ クラスを Python から呼び出してみました。 ctypes モジュールは python2.5 以降では標準で含まれるようです。 まずは C++ クラスのコンストラクタ、デストラクタ、メンバ関数の wrapper 関数を作り、 Python から呼び出してみました。
題材は昨今作成中の詰将棋プログラムです。 以下のサンプル出力では、 まず共有ライブラリ libcshogi.so をロードし、 次にコンストラクタに盤面情報を文字列で渡し、 最後に探索メソッドを呼び出し、解答を出力しています。 ctypes は使い易い。Python 標準実装と云うのも心強いし、 とりあえず決まりかな^^
$ python2.5 >>> import ctypes >>> so=ctypes.CDLL("./libcshogi.so") >>> cp=so.cshogi_create('K32S22S34L11P13P23P44~R2BG3S2N4L3Pe/P54b53p41~G/') 9 8 7 6 5 4 3 2 1 |.|.|.|.|.|p|.|.|L|1 {R2,B,G3,S2,N4,L3,Pe} |.|.|.|.|.|.|K|S|.|2 |.|.|.|.|b|.|.|P|P|3 |.|.|.|.|P|P|S|.|.|4 |.|.|.|.|.|.|.|.|.|5 |.|.|.|.|.|.|.|.|.|6 |.|.|.|.|.|.|.|.|.|7 |.|.|.|.|.|.|.|.|.|8 |.|.|.|.|.|.|.|.|.|9 {G} >>> so.cshogi_solve(cp, 7) G33 K2132 b3153 S3122 p3141 K3121 S32 G33 K2132 b3153 S3122 p3141 K1221 S21
以下の説明では話を簡単にするため、次のような C++ クラス CShogi を考えます。 (実際のファイルは CShogi.h と CShogi.cpp)
#include <cstring> class CShogi { private: char buf[512]; public: CShogi() { printf("CShogi:: (new)\n"); } ~CShogi() { printf("CShogi:: (del)\n"); } void puts(const char* p) { printf("CShogi::puts (%s)\n", p); strcpy(buf, p); } const char* gets() const { printf("CShogi::gets (%s)\n", buf); return buf; } };
まず wrapper 関数を作ります。ソース cshogi.cpp は以下の通り。
#include "cshogi.h" #include "CShogi.h" void* cshogi_create() { return (void*) new CShogi; } void cshogi_delete(void* p) { delete (CShogi*)p; } void cshogi_puts(void* p, const char* s) { ((CShogi*)p)->puts(s); } const char* cshogi_gets(void* p) { return ((CShogi*)p)->gets(); }
ヘッダファイル cshogi.h は C からも呼び出せる形式にします。 次のようになります。
#ifdef __cplusplus extern "C" { #endif void* cshogi_create(void); void cshogi_delete(void*); void cshogi_puts(void*, const char*); const char* cshogi_gets(void*); #ifdef __cplusplus } #endif
次に共有ライブラリを作ります。 ビルドが正常終了したなら libcshogi.so がカレントディレクトリに出来てるはずです。
$ g++ -g -Wall -fPIC -o CShogi.o -c CShogi.cpp $ g++ -g -Wall -fPIC -o cshogi.o -c cshogi.cpp $ g++ -shared CShogi.o cshogi.o -o libcshogi.so
さて、Python から呼び出してみましょう。
$ python2.5 >>> import ctypes >>> so = ctypes.CDLL("./libcshogi.so") >>> cp = so.cshogi_create() CShogi:: (new) >>> so.cshogi_puts(cp, 'abc') CShogi::puts (abc) >>> so.cshogi_gets(cp) >>> so.cshogi_gets.restype = ctypes.c_char_p >>> so.cshogi_gets(cp) CShogi::gets (abc) 'abc' >>> so.cshogi_delete(cp) CShogi:: del
最後に Python のクラスモジュール CShogi.py を作ります。 関数のプロトタイプ宣言が追加されています。 __del__ メソッドにはバッドノウハウを追加。
#! /usr/bin/python2.5 import ctypes from ctypes import c_void_p, c_char_p so=ctypes.CDLL("./libcshogi.so") class CShogi(object): def __init__(self): so.cshogi_create.restype = c_void_p self.v = so.cshogi_create() def __del__(self, so=so): so.cshogi_delete.argtypes = [c_void_p] so.cshogi_delete(self.v) def puts(self, s): so.cshogi_puts.argtypes = [c_void_p, c_char_p] so.cshogi_puts(self.v, s) def gets(self): so.cshogi_gets.argtypes = [c_void_p] so.cshogi_gets.restype = c_char_p return so.cshogi_gets(self.v) if __name__ == '__main__': ob = CShogi() ob.puts('abc') a = ob.gets() ob.puts('xyz') x = ob.gets() print 'a=', a, 'len=', len(a), 'type=', type(a) print 'x=', x, 'len=', len(x), 'type=', type(x)
これを実行した出力は以下の通りです。
$ python2.5 CShogi.py CShogi:: (new) CShogi::puts (abc) CShogi::gets (abc) CShogi::puts (xyz) CShogi::gets (xyz) a= abc len= 3 type= <type 'str'> x= xyz len= 3 type= <type 'str'> CShogi:: (del)