LoginSignup
4
3

More than 3 years have passed since last update.

Pythonを爆速にする「Cython」チュートリアル: C++側の関数がオーバーロードを持つとき。

Posted at

概要

C++ で書いているコードを、どうしてもPythonで呼べる形のライブラリにしたい。
ただ、そこにはCythonという大きな壁が立ちはだかっていた。

これは、Pythonを爆速にする「Cython」チュートリアル: C++のコードで定義したEnumクラスをPython のEnumにパースするやり方。 の続きになります。

コードは「ここのgithub」にあげてあるので、ぜひご覧ください。

前回は、C++で書かれたライブラリをCython化してPythonから使えるようにする際の、C++で宣言されたEnumクラスをPython側のEnumクラスに引き渡す、というところを解説しました。

今回は、C++側の関数がオーバーロードをしているとき(同じ関数名で、引数に異なるものが入るような構成のとき)の、Cython化の解説になります。

状況説明

これまで書いてきたライブラリの、C++側に以下のような関数を作ります。
ここで、 print_vector関数は、引数に vector<int> 及びvector<double>を取ることのできる関数であり、このようなとき関数がオーバーロードされているといいます。

cpp_library/TestClass1.h

class TestClass1{
    private:
        TestClass2 property_test_class2;
        EnumForTestClass1 group;

    public:
        //〜省略
        static void print_vector(vector<int> x);
        static void print_vector(vector<double> x);

中身は以下のようにただvectorの中身をプリントする関数です。

cpp_library/TestClass1.cpp
void TestClass1::print_vector(vector<int> x){
    for(int i=0; i<x.size(); i++){
        cout << x[i] << " ";
    }
    cout << endl;

}

void TestClass1::print_vector(vector<double> x){
    for(int i=0; i<x.size(); i++){
        cout << x[i] << " ";
    }
    cout << endl;

}

Cython化

このprint_vector関数をCythonから例のように呼ぶことを考えてみます。

cython/my_library.pxd
cdef extern from "../cpp_library/TestClass1.h" namespace "my_library":
    cdef cppclass TestClass1:
        # 〜省略
        void print_vector(vector[int] x)
        void print_vector(vector[double] x)    
cython/test_class1.pxd
cdef extern from "../cpp_library/TestClass1.h" namespace "my_library":
    cdef cppclass TestClass1:
        # 〜省略
        void print_vector(vector[int] x)
        void print_vector(vector[double] x)    

ここで、cython/test_class1.pyxに中身を実装していくわけですが、

cython/test_class1.pyx
    @staticmethod
    def print_vector(list x):
        cdef TestClass1 testclass1
        return testclass1.print_vector(x)

としてpython setup.py install を実行しようとすると、

Error compiling Cython file:
------------------------------------------------------------
...
        return testclass1.test_vector_int_ref(x,y) 

    @staticmethod
    def print_vector(list x):
        cdef TestClass1 testclass1
        return testclass1.print_vector(x)
                                     ^
------------------------------------------------------------

cython/test_class1.pyx:51:38: no suitable method found

とエラーが出て怒られます。。考えてみれば当たり前なのですが、C++側のprint_vector関数はオーバーロードされているため、
どの関数を参照しているかわからない、というわけです。
これを解決するため、キャストをして、どの関数をコールしているか明示することが必要になります。

そこで、cython/test_class1.pyxを以下のように書き直します。

cython/test_class1.pyx
    @staticmethod
    def print_vector(list x):
        cdef TestClass1 testclass1

        if isinstance(x[0], int):
            return testclass1.print_vector(<vector[int]>x)
        elif isinstance(x[0], float):
            return testclass1.print_vector(<vector[double]>x)
        else:
            raise Exception('TypeError')

これは、引数であるxをまずはlistと指定したあと、
その中身のタイプによってC++側のどの関数をコールするか指定しています。

実際にこのように書くと、python setup.py install でビルドできて、

test.py
import my_library as myl

if __name__ == "__main__":

    cl1 = myl.test()
    x = [1,2,3]
    y = [1.1, 2.2, 3.3]
    cl1.print_vector(x)
    cl1.print_vector(y)

を実行してテストすると、

(myenv) root@e96f489c2395:/from_local/cython_practice# python test.py 
1 2 3 
1.1 2.2 3.3 

とうまくC++側の関数をコールできていることが分かります。

まとめ

今回は、C++側の関数がオーバーロードをしているとき(同じ関数名で、引数に異なるものが入るような構成のとき)の、Cython化の解説をしました。

だいたい網羅してきた気がする、、あとどのようなことがC++によく書くでしょうか?
次何を書こうか現段階では決まっていないので、ネタが決まり次第次も書こうと思います。

今回はこのへんで。

おわり。

4
3
0

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
4
3