wrapping a c++ library with cython

39
Wrapping a C++ library with Cython Tokyo.SciPy #5 2013-01-26 1 / 39

Upload: fuzzysphere

Post on 10-May-2015

3.982 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Wrapping a C++ library with Cython

Wrapping a C++ library with Cython

Tokyo.SciPy #52013-01-26

1 / 39

Page 2: Wrapping a C++ library with Cython

概要

Cythonを用いたC/C++ライブラリの Pythonラッパー作成Python/C API

の初歩について説明する.

2 / 39

Page 3: Wrapping a C++ library with Cython

Outline

...1 Introduction

...2 Cython

...3 Python/C API

...4 Summary

3 / 39

Page 4: Wrapping a C++ library with Cython

1. Introduction

4 / 39

Page 5: Wrapping a C++ library with Cython

動機

PythonとC/C++の両方の長所を活かしたい

C/C++ 実行速度の速さデータ構造,数値計算ライブラリが揃っている

→計算量が支配的な箇所に使うPython 読み書きしやすさ,柔軟性,拡張性,ポータビリティ

利便性の高いライブラリが揃っている

→その他のすべての箇所に使う

データ収集,前処理コマンドライン, GUIアプリ,ウェブアプリ設定ファイル,ジョブ管理,ログ管理テスト,可視化,ドキュメンテーション, · · ·

⇒ PythonからC/C++を呼び出せればよい5 / 39

Page 6: Wrapping a C++ library with Cython

6 / 39

Page 7: Wrapping a C++ library with Cython

Python/C API

Python/C APIを用いて Pythonの拡張モジュールを作成することにより以下が可能となる.

新しいオブジェクトの追加

C/C++の呼び出し

http://docs.python.jp/2/c-api/index.html

http://docs.python.jp/2/extending/extending.html

7 / 39

Page 8: Wrapping a C++ library with Cython

say.c

#include <Python.h>

static PyObject* say_hello(PyObject* self, PyObject* args) {

const char* name;

if (!PyArg_ParseTuple(args, "s", &name))

return NULL;

printf("Hello %s!\n", name);

Py_RETURN_NONE;

}

...

setup.py

setup(name="say", ext_modules=[

Extension("say", ["say.c"])

])

8 / 39

Page 9: Wrapping a C++ library with Cython

$ python setup.py build_ext --inplace

$ python

>>> import say

Pythonの内部構造についての知識が必要メモリ管理,例外処理などが面倒コード量が多い

⇒拡張モジュールを作成するためのツールを使う

9 / 39

Page 10: Wrapping a C++ library with Cython

拡張モジュールを作成するためのツール

C/C++による拡張モジュールを作成するためのツールとしてPyrexCythonSWIGSIPBoost.Python

などがある.

以下ではCythonを用いてC/C++ライブラリの Pythonラッパーを作成する方法について説明する.

10 / 39

Page 11: Wrapping a C++ library with Cython

2. Cython

11 / 39

Page 12: Wrapping a C++ library with Cython

Cython

Pythonの拡張モジュールを作成するための言語Python +型宣言を基本とした言語仕様CPython 2.4-3.x, Windows/Mac/LinuxApache Licenselxml, Numpy, Scipy, Sage, mpi4py, petsc4py, · · ·http://cython.org/

12 / 39

Page 13: Wrapping a C++ library with Cython

拡張モジュールの作成方法...1 pyxファイルを作成する...2 Cythonを用いて pyxファイルを c/cppファイルに変換する...3 c/cppファイルをコンパイルする

生成された soファイルは Pythonから直接インポートできる.

setup(ext_modules=cythonize("foo.pyx"))

$ python setup.py build_ext --inplace

$ python

>>> import foo

13 / 39

Page 14: Wrapping a C++ library with Cython

Cythonでは Pythonのソースコードが (ほぼ)そのまま使える

さらに以下のような言語仕様が加えられている.型宣言

C/C++の読み込み条件付きコンパイル

コンパイラディレクティブ

etc.

14 / 39

Page 15: Wrapping a C++ library with Cython

型宣言

cdef int i, j[10]

cdef float f, *g

cdef struct Rectangle:

float width

float height

cdef enum State:

open = 1

closed = 2

cdef object pyobj

ctypedef unsigned long uint64_t

from libc.stdint cimport int64_t

15 / 39

Page 16: Wrapping a C++ library with Cython

型変換

基本的な数値型と文字列型については, PythonオブジェクトとC変数が自動変換される.

cdef bytes py_byte_string

cdef unicode py_unicode_string

cdef char* c_string

py_byte_string = <bytes> c_string

py_byte_string = c_string

py_byte_string = c_string[:length]

c_string = py_byte_string

py_unicode_string = py_byte_string.decode("utf-8")

py_byte_string = py_unicde_string.encode("utf-8")

16 / 39

Page 17: Wrapping a C++ library with Cython

関数定義defにより定義

引数,返り値ともに PythonオブジェクトPythonから呼び出せる

cdefにより定義引数,返り値ともにC変数 (Pythonオブジェクトも含む)Pythonから呼び出せない

def integrate(double a, double b, int N):

# 引数, 返り値は自動的に型変換されるcdef int i

cdef double s, dx

s = 0; dx = (b - a) / N

for i in range(N):

s += f(a + i * dx)

return s * dx

cdef float f(double x) except *:

return 1 / x

17 / 39

Page 18: Wrapping a C++ library with Cython

拡張型cdef class Interval:

cdef public float x0, x1

def __init__(self, x0, x1):

self.x0 = x0; self.x1 = x1

@property

def length(self):

return self.x1 - self.x0

def widen(Interval i not None, r):

i.x0 *= r; i.x1 *= r

ビルトイン型,拡張型を継承できる. 多重継承はできない.アトリビュートには public, readonlyを指定できる拡張型の値は Noneを取りうる拡張型の引数には not Noneを指定できる<MyClass?>は型チェック付きキャスト

18 / 39

Page 19: Wrapping a C++ library with Cython

拡張型の初期化

cinit Cレベルの初期化を行う.必ず一度だけ呼び出される.この時点では Pythonオブジェクトとして不完全.

init cinit 以外の初期化を行う.複数回呼ばれる/1回も呼ばれない場合もある.

dealloc Cレベルの破棄処理.この時点では Pythonオブジェクトとして不完全.

基底型の cinit が先に呼び出される.コンストラクタに渡した引数は cinit , init の両方に渡される.

19 / 39

Page 20: Wrapping a C++ library with Cython

C言語とのシンタックスの違い

constは使えない

ヌルポインタは NULLにより表す

p->aの代わりに p.aを使う

&xの代わりに cython.address(x)を使う

*pの代わりに p[0] or cython.operator.dereference(p)を使う

20 / 39

Page 21: Wrapping a C++ library with Cython

Cの読み込み

CythonはCのヘッダファイルを読まないため,以下のような宣言が必要となる.

cdef extern from "math.h":

double sin(double)

double M_PI

def py_sin(d):

# Pythonから呼び出し可能return sin(M_PI / 180.0 * d)

21 / 39

Page 22: Wrapping a C++ library with Cython

C++の読み込み

名前空間

クラス

テンプレート

演算子オーバーロード

ポリモーフィズム

などに対応している.

C++の例外は対応する Pythonの例外に翻訳される.

22 / 39

Page 23: Wrapping a C++ library with Cython

STLコンテナ

STLコンテナは libcpp以下から cimportするだけで使える対応する Python組み込み型があれば自動変換される

from libcpp.string cimport string

cdef string cpp_string

cdef bytes py_byte_string

cpp_string = <string> py_byte_string

cpp_string = py_byte_string

py_byte_string = cpp_string

23 / 39

Page 24: Wrapping a C++ library with Cython

C++クラスの宣言pair.pyx

cdef extern from "<utility>" namespace "std":

cdef cppclass pair[T, U]:

T first

U second

pair() nogil except +

pair(pair&) nogil except +

pair(T&, U&) nogil except +

bint operator==(pair&, pair&) nogil

bint operator!=(pair&, pair&) nogil

...

cdef pair[int, char*] *p = new pair[int, char*](1, "One")

setup.py

setup(ext_modules=cythonize("pair.pyx", language="c++"))

24 / 39

Page 25: Wrapping a C++ library with Cython

Pythonラッパークラスcppclassを Pythonから呼び出すにはラッパークラスが必要.

cdef class PyPair:

cdef pair[int, char*] *thisptr

def __cinit__(self, *args, **kw):

self.thisptr = new pair[int, char*]()

def __init__(self, int i, char* s):

self.thisptr.first = i

self.thisptr.second = s

def __dealloc__(self):

del self.thisptr

@property

def first(self):

return self.thisptr.first

@property

def second(self):

return self.thisptr.second

25 / 39

Page 26: Wrapping a C++ library with Cython

デバッガ

$ python-dbg setup.py build_ext --pyrex-gdb --inplace

$ cygdb

(gdb)

使い方はGDBとほぼ同じ.ブレークポイントの設定

スタックのインスペクション

ステップ実行

etc.

26 / 39

Page 27: Wrapping a C++ library with Cython

typednessのアノテーションCythonおよび C/C++コードを typednessにより色分けしたHTMLファイルを生成する.

$ cython foo.pyx -a

http://docs.cython.org/src/quickstart/cythonize.html

27 / 39

Page 28: Wrapping a C++ library with Cython

プロファイリング

次のディレクティブによりプロファイリングが有効になる.

# cython: profile=True

使用方法は cProfileを使った Pythonのプロファイリングと同じ.

cProfile.runctx("extmod.func()",

globals(), locals(), "Profile.prof")

s = pstats.Stats("Profile.prof")

28 / 39

Page 29: Wrapping a C++ library with Cython

その他の機能

Numpyとの連携Sage Notebookとの連携コンパイラディレクティブ

条件付きコンパイル

融合型

型つきメモリビュー

並列化

GIL制御etc.

詳しくは http://docs.cython.org/

29 / 39

Page 30: Wrapping a C++ library with Cython

3. Python/C API

30 / 39

Page 31: Wrapping a C++ library with Cython

Cythonにより生成されたC/C++ファイルを読むには Python/C APIの知識が必要となる. 以下ではその初歩について説明する.

詳しくは http://docs.python.jp/2/extending/index.html

31 / 39

Page 32: Wrapping a C++ library with Cython

すべてのデータはオブジェクト

#define PyObject_HEAD

_PyObject_HEAD_EXTRA /* デバグ用*/ \Py_ssize_t ob_refcnt; /* 参照カウンタ */ \struct _typeobject *ob_type; /* 型オブジェクト */

typedef struct _object {

PyObject_HEAD

} PyObject;

typedef struct {

PyObject_HEAD

long ob_ival;

} PyIntObject;

PyInt FromLongなどで Pythonオブジェクトを構築PyInt Checkなどで型チェックPyInt AsLongなどでC変数を取得

32 / 39

Page 33: Wrapping a C++ library with Cython

33 / 39

Page 34: Wrapping a C++ library with Cython

Pythonから呼び出す関数の引数,返り値は Pythonオブジェクト

static PyObject *

func(PyObject *self, PyObject *args) {

const char *s;

if (!PyArg_ParseTuple(args, "s", &s))

return NULL;

...

}

typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);

PyErr *で例外処理

if (PyErr_Occurred()) {

if (/* StopIterationだったら */) PyErr_Clear();else { goto __pyx_error; }

}

34 / 39

Page 35: Wrapping a C++ library with Cython

PyObject Call*で Pythonオブジェクトの呼び出し

PyObject* PyObject_Call(PyObject *callable,

PyObject *args, PyObject *kw)

args, kwのチェック,再帰の管理などが行われる

ガベージコレクションは参照カウント法

参照カウントの振る舞い “参照の所有権”により理解されるPy INCREF, Py DECREFで参照カウンタを増減参照カウンタが 0になったオブジェクトは破棄される

35 / 39

Page 36: Wrapping a C++ library with Cython

4. Summary

36 / 39

Page 37: Wrapping a C++ library with Cython

まとめ

Cythonを使ってC/C++ライブラリの Pythonラッパーを“手軽に”作ることが出来る拡張ライブラリの仕組みを把握するにはPython/C APIの知識が必要

CythonはC/C++と Pythonの両方の長所を活かすための

橋渡しとしての役割を果たす

37 / 39

Page 38: Wrapping a C++ library with Cython

References

[1] http://docs.python.jp

[2] http://docs.cython.org/

[3] Cythonユーザメーリングリスト

[4] D. S. Seljebotn, Fast numerical computations with Cython,Proceedings of the 8th Python in Science Conference, 2009.

38 / 39

Page 39: Wrapping a C++ library with Cython

fin.

Revision: 9176288 (2013-01-25)

39 / 39