numpy arrayをPythonとC++の間でやり取りするにはどうすればいいのか?答えはNumpy c APIを使えばいいらしい。このNumpy c APIの使用例が、前回参考にしたサイトに載っていたが、Python2用に書かれたコードなので、当然Python3.6では改変しないと使えない。なので、早速改変してみた。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <boost/python.hpp> #include <numpy/ndarrayobject.h> namespace bp = boost::python; void reference_contiguous_array(PyObject* in, PyArrayObject* &in_con, double* &ptr, int &count) { in_con = PyArray_GETCONTIGUOUS((PyArrayObject*)in); ptr = (double*)PyArray_DATA(in_con); int num_dim = PyArray_NDIM(in_con); npy_intp* pdim = PyArray_DIMS(in_con); count = 1; for (int i = 0; i < num_dim; i++) { count *= pdim[i]; } } void dereference(PyObject* o) { Py_DECREF(o); } PyObject* entry_square_matrix(PyObject* input_matrix) { // get the input array double* ptr; int count; PyArrayObject* input_contigous_array; reference_contiguous_array(input_matrix, input_contigous_array, ptr, count); // create the output array npy_intp dst_dim[1]; dst_dim[0] = count; PyObject* out_matrix = PyArray_SimpleNew(1, dst_dim, NPY_FLOAT64); double* ptr_out; PyArrayObject* output_contigous_array; reference_contiguous_array(out_matrix, output_contigous_array, ptr_out, count); for (int i = 0; i < count; i++) { ptr_out[i] = ptr[i] * ptr[i]; } dereference((PyObject*)input_contigous_array); dereference((PyObject*)output_contigous_array); return out_matrix; } BOOST_PYTHON_MODULE(_func) { import_array(); bp::def("square_matrix", entry_square_matrix); } |
どうすればPython3でも使えるのか?色々調べてみると、このサイトに答えが載っていた。
import_array()は、Python3ではNullを返すのに対し、BOOST_PYTHON_MODULE()ではVoidを返す仕様上の違いにより、コンパイルすると下記のようなエラーを吐き出す。
#define NUMPY_IMPORT_ARRAY_RETVAL NULL
答えは、import array()用のファンクションを別個に作ってやればいいらしい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #include <boost/python.hpp> #include <numpy/ndarrayobject.h> namespace bp = boost::python; void reference_contiguous_array(PyObject* in, PyArrayObject* &in_con, double* &ptr, int &count) { in_con = PyArray_GETCONTIGUOUS((PyArrayObject*)in); ptr = (double*)PyArray_DATA(in_con); int num_dim = PyArray_NDIM(in_con); npy_intp* pdim = PyArray_DIMS(in_con); count = 1; for (int i = 0; i < num_dim; i++) { count *= pdim[i]; } } void dereference(PyObject* o) { Py_DECREF(o); } PyObject* entry_square_matrix(PyObject* input_matrix) { // get the input array double* ptr; int count; PyArrayObject* input_contigous_array; reference_contiguous_array(input_matrix, input_contigous_array, ptr, count); // create the output array npy_intp dst_dim[1]; dst_dim[0] = count; PyObject* out_matrix = PyArray_SimpleNew(1, dst_dim, NPY_FLOAT64); double* ptr_out; PyArrayObject* output_contigous_array; reference_contiguous_array(out_matrix, output_contigous_array, ptr_out, count); for (int i = 0; i < count; i++) { ptr_out[i] = ptr[i] * ptr[i]; } dereference((PyObject*)input_contigous_array); dereference((PyObject*)output_contigous_array); return out_matrix; } #if PY_VERSION_HEX >= 0x03000000 void * #else void #endif initialize() { import_array(); } BOOST_PYTHON_MODULE(_func) { initialize(); bp::def("square_matrix", entry_square_matrix); } |
こうすることで何とかパイソン用のライブラリが出来上がった。
boost.pythonはC++とPythonを同時に学びたい人にはうってつけの題材かもしれない。
WSLを1週間試してみた結果は、かなり使えるということだ。QT CreatorやSublime Textも動くし、たいていのIDE(VS Codeは動かない)は動くので、プログラミングの勉強には使い勝手がいい。マシン性能が乏しい人にはWSLは有りかもしれない。Visual Studio 2017 communityとUnityやUE4を使えばいいやんと言う人もいるが、例えば、メモリが4GでHDDの空き容量が40Gバイトしかない人間には、そのような大規模なアプリケーションの組み合わせはかなりハードルが高いと言える。