プログラミング:boost.pythonでモジュール構築(その2)

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を返す仕様上の違いにより、コンパイルすると下記のようなエラーを吐き出す。

error: return-statement with a value, in function returning 'void' [-fpermissive]
#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バイトしかない人間には、そのような大規模なアプリケーションの組み合わせはかなりハードルが高いと言える。

スポンサーリンク