##概要
C++ で書いているコードを、どうしてもPythonで呼べる形のライブラリにしたい。
ただ、そこにはCythonという大きな壁が立ちはだかっていた。
これは、Pythonを爆速にする「Cython」チュートリアル: C++側の関数がオーバーロードを持つとき。 の続きになります。
コードは「ここのgithub」にあげてあるので、ぜひご覧ください。
前回は、C++側の関数がオーバーロードをしているとき(同じ関数名で、引数に異なるものが入るような構成のとき)の、Cython化の解説しました。
今回は、C++側の関数が、引数として値渡しではなく参照渡しのときのCython側でのハンドリング仕方を解説したいと思います。
あまり新しいことは出てきませんが、実際Cythonを書こうとするとエラーに悩まされてしまうことが多いので、そのようなときに是非ご覧ください!!
##状況説明
- C++側の関数がオーバーロードされていたので、前回の記事のようにキャストを使って解決していた。
- C++側に実装された関数の引数の中に、参照渡しのものが含まれている。
- 引数が参照型だとエラーでるやん。
実際に試してみます。
class TestClass1{
private:
TestClass2 property_test_class2;
EnumForTestClass1 group;
public:
//~省略
static void print_vector(vector<int> &x);
static void print_vector(vector<double> &x);
前回と違うところは、print_vector
の引数のx
が、値渡しから参照渡しになっているところです。
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;
}
これをいつものようにpython setup.py install
でbuildしようとすると、以下のエラーがでます。
./cython/my_library.cpp:2414:69: error: cannot bind non-const lvalue reference of type 'std::vector<int>&' to an rvalue of type 'std::vector<int>'
__pyx_t_1 = __Pyx_void_to_None(__pyx_v_testclass1.print_vector(((std::vector<int> )__pyx_t_4))); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 53, __pyx_L1_error)
キャストをきちんとしているはずなのに、参照渡しになると怒られます。
そこで、解決策として、
キャストではなく実体をその場で作ります。
どういうことかというと、
@staticmethod
def print_vector(list x):
cdef:
TestClass1 testclass1
vector[int] vec_int
vector[double] vec_double
if isinstance(x[0], int):
vec_int.resize(len(x))
for i in range(len(x)):
vec_int[i] = <int>x[i]
return testclass1.print_vector(vec_int)
elif isinstance(x[0], float):
vec_double.resize(len(x))
for i in range(len(x)):
vec_double[i] = <double>x[i]
return testclass1.print_vector(vec_double)
else:
raise Exception('TypeError')
とします。ここで、cdef
により、vector[int]
や、vector[double]
をその場で作り、それを関数に渡します。
たぶん、さっき出たエラーは、参照渡しなのに、本当に実体存在してるかわからんやん!困るで!!!
というエラーだったんだと自分は解釈しています。
したがって、実体を用意してあげて、安心させるといいわけです。
こうすると、例によって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++側の関数が、引数として値渡しではなく参照渡しのときのCython側でのハンドリング仕方を解説しました。
今回はこのへんで。
おわり。