how to return numpy.array from boost::python?
UPDATE: the library described in my original answer (https://github.com/ndarray/Boost.NumPy) has been integrated directly into Boost.Python as of Boost 1.63, and hence the standalone version is now deprecated. The text below now corresponds to the new, integrated version (only the namespace has changed).
Boost.Python now includes a moderately complete wrapper of the NumPy C-API into a Boost.Python interface. It's pretty low-level, and mostly focused on how to address the more difficult problem of how to pass C++ data to and from NumPy without copying, but here's how you'd do a copied std::vector return with that:
#include "boost/python/numpy.hpp"
namespace bp = boost::python;
namespace bn = boost::python::numpy;
std::vector<double> myfunc(...);
bn::ndarray mywrapper(...) {
std::vector<double> v = myfunc(...);
Py_intptr_t shape[1] = { v.size() };
bn::ndarray result = bn::zeros(1, shape, bn::dtype::get_builtin<double>());
std::copy(v.begin(), v.end(), reinterpret_cast<double*>(result.get_data()));
return result;
}
BOOST_PYTHON_MODULE(example) {
bn::initialize();
bp::def("myfunc", mywrapper);
}
How to create a numpy array of boost::python::object types
You're using the dtype
constructor correctly; it's obj
that's causing the trouble.
The default construction boost::python::object obj;
sets obj
as the 'None'
Python object. The dtype
associated with 'None'
is a NPY_DEFAULT
array descriptor type. And that maps to a double
when creating the numpy
array, which explains your output. (That makes sense from a Python perspective - the default numpy
array type is a double precision floating point type.)
You can constuct a dtype
with an object type (NPY_OBJECT
) using
boost::python::numpy::dtype dt = boost::python::numpy::dtype(boost::python::object("O"));
which, in your case, is the fix. I've also taken the liberty of using an anonymous temporary which is how it's done in the Boost documentation. The "O"
denotes the object type.
boost python - nullptr while extracting ndarray
That error occurs since you're using the numpy
module without first initializing it.
Notice the beginning of the official tutorial:
Initialise the Python runtime, and the numpy module. Failure to call these results in segmentation errors:
namespace np = boost::python::numpy;
int main(int argc, char **argv)
{
Py_Initialize();
np::initialize();
Your code is lacking the call to np::initialize();
.
Passing boost::python::numpy::ndarray as (default or not) argument of a boost::python function?
To be able to use numpy
first initialise the Python
runtime and the numpy
module:
Py_Initialize();
np::initialize();
Failure to call these results in segmentation errors.
=======================================================================
For the silly.cpp
the workaround is not needed. Here is the implementation:
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
namespace bp = boost::python;
namespace np = boost::python::numpy;
int f(np::ndarray x, np::ndarray y = np::array(bp::list()) ) {
return (int)(x.shape(0)+y.shape(0));
};
BOOST_PYTHON_MODULE(silly)
{
Py_Initialize();
np::initialize();
bp::def("f", f, ( bp::arg("x"), bp::arg("y") = np::array(bp::list()) ) );
}
And the test results:
>>> import numpy, silly; silly.f(numpy.array([1, 2, 3]), numpy.array([1, 2]))
5
>>> import numpy, silly; silly.f(numpy.array([1, 2, 3]))
3
Passing C structs to python as numpy arrays using boost python
Ok, I've managed to answer my own question. It wasn't straight-forward, but it works now...
#include <numpy/arrayobject.h>
void MyBaseClass::myCallback(const MyRecord& data)
{
object func = get_override("myCallback");
if (func) {
PyArray_Descr* dtype;
PyObject* op = Py_BuildValue("[(s,s),(s,s),(s,s)]", "myInt", "<u4", "myString", "|S4", "myDouble", "<f8");
PyArray_DescrConverter(op, &dtype);
Py_DECREF(op);
PyObject* pya = PyArray_FromString(const_cast<char*>(reinterpret_cast<const char*>(&data)), sizeof(data), dtype, 1, NULL);
// PyObject_Print(pya, stdout, 0);
numeric::array bpa(static_cast<numeric::array>(handle<>(pya)));
func(bpa);
}
}
BOOST_PYTHON_MODULE(example3)
{
import_array()
class_<MyBaseClass>("MyBaseClass")
.def("myCallback", &MyBaseClass::myCallback);
}
Thanks again,
Paul
Related Topics
Insert VS Emplace VS Operator[] in C++ Map
How to See the Assembly Code for a C++ Program
Can the Use of C++11's 'Auto' Improve Performance
Std::Auto_Ptr to Std::Unique_Ptr
What Would Be C++ Limitations Compared C Language
How Boost::Function and Boost::Bind Work
Pimpl Idiom VS Pure Virtual Class Interface
Passing Shared Pointers as Arguments
In C++, Is It Still Bad Practice to Return a Vector from a Function
Super High Performance C/C++ Hash Map (Table, Dictionary)
Why Do We Actually Need Private or Protected Inheritance in C++
How to Find Where an Exception Was Thrown in C++
C/C++ Function Definitions Without Assembly
How Define an Array of Function Pointers in C
How to Calculate a Time Difference in C++
How to Overload the Operator++ in Two Different Ways for Postfix A++ and Prefix ++A