You have a C++ std::vector and you want to convert it to a Boost.Python Numpy ndarray.

But, once the ndarray got, you want to get back to the C++ array.

How to do that?

Let’s see that in this Boost.Python tutorial.

First of all

We need to install Python 3 and Boost on your computer.

So in order to have the exact same software and libraries installed in the exact same locations, I suggest to follow the 2 following tutorials:

  1. Python:
  2. Boost:

C++, std::vector, Boost.Python, Numpy and ndarray

From C++ array to ndarray

In order to use data, from a standard C++ vector to a Numpy ndarray, we need to get the address of the first element in this vector.

The reason is that we are going to use the from_data() method to transform our C++ vector to a Python array.

And this from_data() method needs the address of the first element of an array.

So it would have been possible to use the following:

  • &originalVector[0]

But we will prefer using:

  • originalVector.data()

Once we have our C++ array, it’s quite easy to convert it into a Numpy ndarray.

Shape and strides

In the from_data() method we’ll set the

  • shape with : python::make_tuple(1, sizeOfVector)
  • strides with: python::make_tuple(sizeof(float) * 0, sizeof(float) * 1)

In this example:

  • 1 = shape(0), the number of rows;
  • sizeOfVector = shape(1), the number of columns.

Then

  • sizeof(float) * 0 = strides(0) = the number of element to jump from a row to the next one
  • sizeof(float) * 1 = strides(1) = the number of element to jump from a column to the next one

So to get the number of elements in the ndarray we can use shape(1) because it holds the size of the vector.

From ndarray to C++ array

Once we get an ndarray, if we want to come back to a C++ array, we have to use the reinterpret_cast expression.

Indeed, the ndarray::get_data() method returns a char*.

So we use this mechanism to tranform this char* into the type of our choice.

In our case into a float*.

Then with this float* we can do whatever we want.

In the example we modify data from the C++ array by dividing all elements by 10.

And as the ndarray passed as parameter is a pointer, so the original ndarray is also modified.

Checking with contains()

In bonus, we check with the Boost.Python contains() method if a float is present in the final array.

Let’s code a bit

// badprog.com

#include “pch.h”

//

#include <boost/python/numpy.hpp>

//

#include

#include

//

namespace python = boost::python;

namespace numpy = boost::python::numpy;

// —————————————————————————-

//

// —————————————————————————-

void changeArrayOriginalFromPyArray(numpy::ndarray *py_array) {

//

float *arrayData = reinterpret_cast<float *>(py_array->get_data());

int i = 0;

//

arrayData[2] = 1337.4;

// we need shape(1) because this returns the number of elements

while (i < py_array->shape(1)) {

//

arrayData[i] /= 10;

++i;

}

}

// —————————————————————————-

//

// —————————————————————————-

void displayOriginalVector(std::vector &originalVector) {

//

for (auto &element : originalVector) {

//

std::cout « element « ’ ‘;

}

//

std::cout « std::endl;

}

// —————————————————————————-

//

// —————————————————————————-

void displayPyArray(numpy::ndarray &py_array) {

//

std::cout « “py_array: " « python::extract<char const *>(python::str(py_array)) « std::endl;

}

// —————————————————————————-

//

// —————————————————————————-

int main() {

//

Py_Initialize();

numpy::initialize();

//

std::vector originalVector = { 1.5, 2.5, 3.5, 4.5, 5.5 };

int sizeOfVector = originalVector.size();

float *originalArray = originalVector.data();

//

std::cout « “sizeOfArray = " « sizeOfVector « std::endl;

//

numpy::dtype arrayType = numpy::dtype::get_builtin();

//

numpy::ndarray py_array = numpy::from_data(

originalArray,

arrayType,

python::make_tuple(1, sizeOfVector),

python::make_tuple(sizeof(float) * 0, sizeof(float) * 1),

python::object()

);

//

// 1 ————————————————————————-

//

std::cout « “C++ array: “;

//

displayOriginalVector(originalVector);

//

displayPyArray(py_array);

//

changeArrayOriginalFromPyArray(&py_array);

//

// 2 - Modifying py_array

//

std::cout « “C++ array: “;

//

displayOriginalVector(originalVector);

//

displayPyArray(py_array);

//

// 3 - Modifying originalVector

//

originalVector.at(3) = 911;

//

std::cout « “C++ array: “;

//

displayOriginalVector(originalVector);

//

displayPyArray(py_array);

//

// 4 - Containing

//

float valueToFind = 1.5 / 10;

//

std::cout « “py_array: " « python::extract<char const *>(python::str(py_array.contains(1337))) « std::endl;

std::cout « “py_array: " « python::extract<char const *>(python::str(py_array.contains(valueToFind))) « std::endl;

//

return 0;

}

As a result you’ll have from the console:

sizeOfArray = 5
C++ array: 1.5 2.5 3.5 4.5 5.5
py_array: [[1.5 2.5 3.5 4.5 5.5]]
C++ array: 0.15 0.25 133.74 0.45 0.55
py_array: [[ 0.15 0.25 133.74 0.45 0.55]]
C++ array: 0.15 0.25 133.74 911 0.55
py_array: [[1.5000e-01 2.5000e-01 1.3374e+02 9.1100e+02 5.5000e-01]]
py_array: False
py_array: True

Conclusion

You are now able to manipulate C++ vector, array and Numpy ndarray like if they were the same object.

Good job, you did it.