How to access numpy array within PyCXX

461 Views Asked by At

I would like to convert numpy array to some double* or stl vector on the c++ side. I'm actually using PyCXX for this and I can't figure out the way to access the data.

I'm currently able to access and return the data buffer like this :

Py::Object arrayShape(const Py::Tuple& args ){
     Py::Object array= args[0];
     return array.getAttr("data");
}

But I don't know what to do with it. My final goal, is to obtain a gsl_vector out of it. Ideally, I wouldn't have to recopy the memory. But maybe it is too much to ask ;)

1

There are 1 best solutions below

0
On

When I am searching for a solution and I can find only others posting the same, long unanswered question, I post the solution once I discover it. Yours is such a question.

Firstly, strongly consider using Cython as your glue and proceed no further down this dangerous path.

That out of the way, using PyArray_FromAny will give you a view of the underlying data if possible and a copy otherwise. A very simple example (build with -std=c++11 if you are an honest and good person, or VS2013 if you are a Windows user):

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>

Py::Object printNumpyArrayCxxFunction(const Py::Tuple& args)
{
    if(args.length() != 1)
    {
        throw Py::RuntimeError("Exactly one argument required.");
    }
    PyObject* vector_{PyArray_FromAny(*args[0], PyArray_DescrFromType(NPY_DOUBLE), 1, 1, NPY_ARRAY_CARRAY_RO, nullptr)};
    if(!vector_)
    {
        throw Py::ValueError("Failed to convert argument into a 1d numpy double (64-bit float) array.");
    }
    Py::Object vector(vector_, true);
    PyArrayObject* vector_npy{reinterpret_cast<PyArrayObject*>(vector_)};
    npy_intp vector_length{PyArray_SIZE(vector_npy)};
    double*const vector_begin{reinterpret_cast<double*>(PyArray_DATA(vector_npy))};
    double*const vector_end{vector_begin + vector_length};

    for(double* vector_iterator{vector_begin}; vector_iterator != vector_end; ++vector_iterator)
    {
        if(vector_iterator != vector_begin)
        {
            std::cout << ", ";
        }
        std::cout << *vector_iterator;
    }
    std::cout << std::endl;

    return Py::None();
}

Note the true argument as the second parameter to Py::Object constructors for "owned" objects! An example of a cpython3 extension that uses the Numpy C API in combination with PyCXX with cmake for building. The link is to a specific commit because I am considering switching this extension back to using Cython.