Cythonベースチュートリアル(5)-C++ライブラリをcythonでパッケージ

12143 ワード

C++ライブラリをcythonで包装
単純に乱数を生成するクラスがあると仮定し、インタフェースを提供します.
namespace mtrandom {

const static unsigned int N = 624;

class MT_RNG {
    public:
        MT_RNG();
        MT_RNG(unsigned long s);
        MT_RNG(unsigned long init_key[], int ket_length);
    
    void init_genrand(unsigned long s);
    
    unsigned long genrand_int32();
    
    double genrand_real1();
    
    private:
        unsigned long mt[N];
        int mti;
};
}

Cythonはpublicメソッドとメンバーのみをパッケージでき、privatedまたはprotectedメソッドとメンバーにアクセスできません.Cythonでこのクラスインタフェースを使用するには、externキーワードを使用するには、3つの要件があります.
  • Declaring the c++ namespace with cython namespace clause
  • Using the cppclass keyword to declare a C++ class interface block
  • Declaring the class’s interface in this block

  • だってMT_RNGはmtrandomネーミングスペースで宣言され、cythonでcdef externを使用するにはネーミングスペースを説明する必要があります.externコードの速い内部で定数Nを宣言し、cppclassキーワードでMT_を宣言します.RNG c++クラスで、そのクラスの内部には、cythonで使用したいPublic構造体、関数、クラスメンバーをすべて入れて、Cythonコードにアクセスできます.
    cdef extern from "mt19937.h" namespace "mtrandom":
        unsigned int N
        cdef cppclass MT_RNG:
            MT_RNG(unsigned long s)
            MT_RNG(unsigned long init_key[], int key_length)
            void init_genrand_int32()
            unsigned long genrand_int32()
            double genrand_real1()
    

    cythonでは、c++クラスを拡張タイプでパッケージするのが一般的です.拡張タイプRNGを定義し、この拡張タイプには、包装されたc++クラスを指すポインタがあり、_cinit__方式で初期化を行い、_dealloc__メモリ回収を行います.dealloc__プログラムが終了したときにのみ使用され、一度だけ呼び出されます.クラスメンバー関数のいくつかをcpdefで定義して乱数を生成できます(_thisptrポインタを介して).次に、c++クラスをパッケージする例を示します.
    cdef class RNG:
        cdef MT_RNG *_thisptr
        def __cinit__(self, unsigned long s):
            self._thisptr = new MT_RNG(S)
        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()
    

    コンパイル
    c++プロジェクトをコンパイルするときは、c++言語を使用し、c++のソースファイルを含むことを説明する必要があります.例えば、RNGをコンパイルする.pyxファイル、setupを新規作成する必要があります.py、含まれるコードは次のとおりです.
    from distutils.core import setup, Extension
    from Cython.Build import cythonize
    
    ext = Extension("RNG", source=['RNG.pyx', 'mt19937.cpp'], language='c++')
    
    setup(name="RNG", ext_modules=cythonize(ext))
    

    リロード関数とメソッド
    MT_の場合RNGクラスでは、異なるパラメータが異なる構造関数に対応していますが、pythonでは、どのように呼び出すのでしょうか.pythonは関数のリロードをサポートしていないため、_cinit__に表示する情報を制限しました.このため、python標準ライブラリの内蔵array(内蔵要素タイプが同じであることが必要)が必要です.cythonはpythonとc++でarrayタイプの変数を処理する方法を知っています.
    from cython.array import array 
    
    cdef class RNG:
        cdef MT_RNG *_thisptr
        def __cinit__(self, seed_or_state):
            if isinstance(seed_or_state, int):
                self._thisptr = new MT_RNG(seed_or_state)
            else:
                state_arr = array("L", seed_or_state)
                self._thisptr = new MT_RNG(state_arr.data.as_ulongs, len(state_Arr))
    
    

    cythonコードで直接C++クラスを使用
    拡張タイプでc++クラスをパッケージする方法を知っています.これもcythonでc++クラスを使用する最も一般的な方法です.もちろん、Cythonコードの中で直接C++クラスを使うこともできます.stack-allocateを使ってMT_を1つ使うことができます.RNGインスタンスは、プログラムの終了時にこのインスタンスをクリーンアップする.c++オブジェクトを使用するには、cef cppclassコードブロックで宣言する必要があります.
    cdef extern from "mt19937.h" namespace "mtrandom":
        cdef cppclass MT_RNG:
            MT_RNG()
            void init_genrand(unsigned long s)
    

    関数でMT_をRNGオブジェクトはいくつかの操作を行う
    def make_random_list(unsigned long seed, unsigned int len):
        cdef:
            #...
            MT_RNG *rng
        rng = new MT_RNG(seed)
        try:
            #...
        finally:
            del rng