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

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

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

[[python高速化]pybind11によるc++組み込み](https://qiita.com/exy81/items/e309df7e33d4ff20a91a)

---

[「Cython　Cとの融合によるPythonの高速化」](https://www.oreilly.co.jp/books/9784873117270/)オライリーから
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)

```py: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)

```py: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のメソッドの定義を記述している。

```py: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()
```

```py: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