LoginSignup
12
15

More than 3 years have passed since last update.

C++をPythonから使えるようにCythonでwrapする

Last updated at Posted at 2015-07-30

注意 以下の記事は古くなっています。

pybind11の使用を検討してください。

[python高速化]pybind11によるc++組み込み


「Cython Cとの融合によるPythonの高速化」オライリーから
C++言語で書かれた関数をPythonでimportして実行できることをUbuntu上でたどってみた。
まずは、この本のサンプルがgithub上にあるのでzipファイルでまとめてダウンロードする。
https://github.com/cythonbook/examples
展開してできたディレクトリに移動する。
examples-master/08-wrapping-cxx/01-simple-example-mt_rng-class

C++で書かれたソースコード
mt19937.h
mt19937.cpp

Cythonにおける外部Cコードの宣言(拡張子 .pxd)
C++クラスのラップ(拡張子 .pyx)

RNG.pyx
# distutils: language = c++
# distutils: sources = mt19937.cpp

cdef extern from "mt19937.h" namespace "mtrandom":
    unsigned int N
    cdef cppclass MT_RNG:
        MT_RNG()
        MT_RNG(unsigned long s)
        MT_RNG(unsigned long init_key[], int key_length)
        void init_genrand(unsigned long s)
        unsigned long genrand_int32()
        double genrand_real1()
        double operator()()

cdef class RNG:

    cdef MT_RNG *_thisptr

    def __cinit__(self, unsigned long s):
        self._thisptr = new MT_RNG(s)
        if self._thisptr == NULL:
            raise MemoryError()

    def __dealloc__(self):
        if self._thisptr != NULL:
            del self._thisptr

    cpdef unsigned long randint(self):
        return self._thisptr.genrand_int32()

    cpdef double rand(self):
        return self._thisptr.genrand_real1()

コンパイルの方法を指示するsetup.pyファイル

py.setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize

ext = Extension("RNG",
                sources=["RNG.pyx", "mt19937.cpp"],
                language="c++")

setup(name="RNG",
      ext_modules=cythonize(ext))

ここから後は、Cythonの解釈を試みているが間違っている可能性があるので
同書を参考してください。

Cythonにおける外部C++コードの宣言(拡張子 .pxd)

pxdファイルの書き方
cdef extern from "C++でのヘッダファイル" namespace "C++での名前空間":
    C++のヘッダファイルの中でconst static で定義されている定数
    unsigned int N
    cdef cppclass C++でのクラス識別子:
        コンストタクタ()
        コンストタクタ(unsigned long s)
        コンストタクタ(unsigned long init_key[], int key_length)
        publicのメソッドのうちCythonで利用するC++のメソッドの宣言
        void init_genrand(unsigned long s)
        unsigned long genrand_int32()
        double genrand_real1()
        double operator()()

githubからダウンロードしたファイルでは、Cythonにおける外部C++コードの宣言は
pyxファイルに移行していること。

pyxファイルにC++言語であることを指定する記述が追加
# distutils: language = c++

cdef cppclass MT_RNG:

のブロックの記述で、参照元のC++でのクラスとpublicのメソッドの定義を記述している。

pyxファイルの書き方
cdef class Cythonでのクラスの識別子:

    cdef MT_RNG *_thisptr

    def __cinit__(self, unsigned long s):
        コンストラクタの記述
        この中でwrapする対象のC++のクラスのインスタンスを生成する
        引数の最初selfはPythonのクラスの場合と共通
        self._thisptr = new MT_RNG(s)
        if self._thisptr == NULL:
            raise MemoryError()

    def __dealloc__(self):
        クリーンアップを行う特殊メソッド
        if self._thisptr != NULL:
            del self._thisptr

    cpdef Cythonでの型 メソッド名(self, 引数):
         Cythonの文法でのメソッドの実装
     この中ではcdef externで宣言したC++の関数やメソッドが利用できる
         return 戻り値
    cpdef unsigned long randint(self):
        return self._thisptr.genrand_int32()

    cpdef double rand(self):
        return self._thisptr.genrand_real1()
setup.pyの書き方
from distutils.core import setup, Extension
from Cython.Build import cythonize

ext = Extension("python拡張モジュールの名前",
                sources=["pyxファイルの名前", "cppファイルの名前"],
                language="c++")

setup(name="RNG",
      ext_modules=cythonize(ext))

C版との違い:
ext = Extension()にlanguage="c++"が追加されたこと。

補足:
Cレベル言語要素の宣言をpxdファイルではなくて、pyxファイルに含めることも可能であること。
しかし、メンテナンスのためにはpxdファイルとして別ものにした方がよいこと。
ためしに、
cdef extern from "mt19937.h" namespace "mtrandom":
で始まるブロックをpyxファイルからpydファイルに抜き出してコンパイルしても
buildと動作に成功した。

URL:
Cython ドキュメント(和訳) チュートリアル
http://omake.accense.com/static/doc-ja/cython/src/tutorial/index.html

12
15
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
15