swig(python編)
とりあえずpython2から。
VC9ExpressEditionで本家のcpythonバージョン2.6向けのモジュールをビルドしたメモ。
swigは最初cygwinのものを使ったが、バージョンが1.3.38とちょっと古かったのでswigのサイトでwindows向けビルド(1.3.40)を落としてきた。ついでなので2つのswigの出力結果を比べてみたところわりと違った。たぶん改行コードの違いとバージョンの違いが主だと思うが、最新版のWindowsビルドを使うほうが無難な気がする。
後で使う予定のポリゴンローダを模した練習用のC++コード
main.cpp
#include "loader.h" #include <iostream> int main(int argc, char **argv) { Loader l; if(!l.read(argv[1])){ return 1; } std::cout << l.getPath() << std::endl; return 0; }
loader.h
#ifndef LOADER_H_INCLUDED #define LOADER_H_INCLUDED #include <vector> #include <string> struct Vertex { float x; float y; float z; }; struct Loader { std::vector<Vertex> vertices; std::string path; bool read(const char *path); std::string getPath()const; int getVertexCount()const; Vertex getVertex(int index)const; }; #endif // LOADER_H_INCLUDED
loader.cpp
#include "loader.h" bool Loader::read(const char *_path) { path=_path; vertices.push_back(Vertex()); vertices.back().x=0; vertices.back().y=1; vertices.back().z=2; vertices.push_back(Vertex()); vertices.back().x=3; vertices.back().y=4; vertices.back().z=5; vertices.push_back(Vertex()); vertices.back().x=6; vertices.back().y=7; vertices.back().z=8; return true; } int Loader::getVertexCount()const { return vertices.size(); } Vertex Loader::getVertex(int index)const { return vertices[index]; } std::string Loader::getPath()const { return path; }
これに対してswigスクリプトインターフェースを書く
loader.i
%module loader %{ #include "loader.h" %} %include "loader.h"
# setup.py from distutils.core import setup, Extension setup(name="loader", py_modules=['loader'], ext_modules=[Extension("_loader", ["loader.i","loader.cpp"], swig_opts=['-c++'], )] )
モジュール作成。
$ python setup.py build $ ls build/lib.win32-2.6/ _loader.pyd
自動的にswigを呼び出してcモジュールの作成までやってくれる。
テスト
test.py
import loader import sys l=loader.Loader() if not l.read("hoge.mqo"): print "fail" sys.exit() for i in xrange(l.getVertexCount()): print l.getVertex(i) print l.getPath()
カレントに_loader.pydをコピーして実行。
$ python test.py <loader.Vertex; proxy of <Swig Object of type 'Vertex *' at 0x00DD1D60> > <loader.Vertex; proxy of <Swig Object of type 'Vertex *' at 0x00DD1D40> > <loader.Vertex; proxy of <Swig Object of type 'Vertex *' at 0x00DD1D60> >
エラー
<Swig Object of type 'std::string *' at 0x00DD1D20>swig/python detected a memory leak of type 'std::string *', no destructor found.
std::stringを返す関数でエラーが出る。
std::string向け
%include "std_string.i"
をloader.iに追加する。
%module loader %{ #include "loader.h" %} %include "std_string.i" %include "loader.h"
再度ビルドしてからカレントに_loader.pydをコピーして実行。
$ python test.py <loader.Vertex; proxy of <Swig Object of type 'Vertex *' at 0x00DD1F80> > <loader.Vertex; proxy of <Swig Object of type 'Vertex *' at 0x00DD1F60> > <loader.Vertex; proxy of <Swig Object of type 'Vertex *' at 0x00DD1F80> > hoge.mqo
pythonの__str__メソッド追加例
print出力の
をカスタマイズする。
loader.i
%extend Vertex { char *__str__() { static char str[256]; sprintf(str, "[%f, %f, %f]", $self->x, $self->y, $self->z); return str; } };
%extendでc++に無いものをpythonモジュールに追加することができる。
$selfがc++のthisのように振る舞う。
文字列の入れ物がstatic char[]でいいのかどうかは良く判らない。
%extend Vertex { std::string __str__() { std::stringstream ss; ss << '[' << $self->x << ',' << $self->y << ',' << $self->z << ']'; return ss.str(); } };
std::string方式。
#include "loader.h"
の下に
#include
を追加しておく。
$ python test.py [0.000000, 1.000000, 2.000000] [3.000000, 4.000000, 5.000000] [6.000000, 7.000000, 8.000000] hoge.mqo
__str__でprint出力変更に成功。
pythonのgeneratorでstd::vectorを回す
for i in xrange(l.getVertexCount()): print l.getVertex(i)
を
for v in l.vertices: print v
と書きたい。
swigインターフェースに追加
%include "std_vector.i" %template(VertexVector) std::vector<Vertex>;
これでvectorにアクセス可能になる。
VertexVectorは名前なので適当に。
generator
http://reference-man.blogspot.com/2010/02/easy-python-generators-using-swig.html
std::vectorについて追記など(5/5)