##概要
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>
を取ることのできる関数であり、このようなとき関数がオーバーロードされているといいます。
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の中身をプリントする関数です。
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から例のように呼ぶことを考えてみます。
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)
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
に中身を実装していくわけですが、
@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
を以下のように書き直します。
@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
でビルドできて、
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++によく書くでしょうか?
次何を書こうか現段階では決まっていないので、ネタが決まり次第次も書こうと思います。
今回はこのへんで。
おわり。