Python * for boost :: python :: object

I am trying to create a Python module in C ++ that converts a 2D vector into a two-dimensional Numpy array. What is wrong here - presumably, there is some conversion needed to raise a python object from PyObject *?

boost::python::object build_day(int year, int day) { PyObject* arr; const int HEIGHT = 5; const int WIDTH = 5; std::vector<std::vector<float> > array(WIDTH, std::vector<float>(HEIGHT)); npy_intp dims[2] = {WIDTH, HEIGHT}; arr = PyArray_SimpleNewFromData(2, dims, NPY_FLOAT, &array); return arr; } BOOST_PYTHON_MODULE(sumpar) { using namespace boost::python; def("build_day", build_day, args("year", "day")); } 
+5
source share
2 answers

boost::python::object provides a generic interface for Python objects. To build one of a PyObject* , you first need to build boost::python::handle<> , which is essentially a smart pointer designed to manipulate Python counted objects ( PyObject* or derived types). Often used handle<> between the boundary between the higher level of Boost.Python code and the Python / C API.

 namespace python = boost::python; PyObject* py_object = get_py_object(); python::handle<> handle(py_object); boost::python object(handle); 

Note that handle will share ownership of PyObject* , and during destruction, it will decrease the reference count for PyObject that it controls. Thus, when building, it is important to indicate whether handle<> increase the reference count of PyObject* .

If PyObject has already increased the reference count, use:

 namespace python = boost::python; PyObject* py_object = ...; python::handle<> handle(py_object); python::object object(handle); 

If PyObject not increased the reference count, and the handle should do this, use the borrowed() function during construction

 namespace python = boost::python; PyObject* py_object = ...; python::handle<> handle(python::borrowed(py_object)); python::object object(handle); 

Here is a complete example demonstrating building a boost::python::object from PyObject* :

 #include <vector> #include <boost/python.hpp> // Mocks... enum { NPY_FLOAT }; typedef int npy_intp; PyObject* PyArray_SimpleNewFromData(int, npy_intp*, int, void*) { return PyString_FromString("hello world"); } boost::python::object build_day(int year, int day) { const int HEIGHT = 5; const int WIDTH = 5; std::vector<std::vector<float> > array( WIDTH, std::vector<float>(HEIGHT)); npy_intp dims[2] = {WIDTH, HEIGHT}; namespace python = boost::python; PyObject* arr = PyArray_SimpleNewFromData(2, dims, NPY_FLOAT, &array); python::handle<> handle(arr); return python::object(handle); } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("build_day", &build_day, python::args("year", "day")); } 

Interactive use:

 >>> import example >>> day = example.build_day(1, 2); >>> assert(day) 

Note that to create a minimal complete example, the above example has mocked PyArray_SimpleNewFromData() , which simply returns a Python string. It is important to consult the documentation to determine if PyObject* borrowed or not, and if there are any life requirements between the object and its arguments. In the case of PyArray_SimpleNewFromData() returned PyObject* :

  • already increased the number of links
  • the lifetime of the underlying memory provided to the array should be at least as long as PyObject is returned. The build_day() function in the original question does not meet this requirement.
+4
source

My suggestion is to use the variables and objects provided by boost :: python, so if you want to return the array to python it might be nice to use boost :: python :: dict, something like ...

 boost::python::dict arr; int i = 0; for (auto &item: array) { arr[i] = item; ++i; } return arr; 
0
source

All Articles