How to make c++ return 2d array to python

Question:

I find an example showing how to return a 1D array from c++ to python. Now I hope to return a 2D array from c++ to python.
I imitate the code shown in the example and my code is as follows:

The file a.cpp:

#include <stdio.h>
#include <stdlib.h>

extern "C" int* ls1(){
    int *ls = new int[3];
    for (int i = 0; i < 3; ++i)
    {
        ls[i] = i;
    }
    return ls;
}
extern "C" int** ls2(){
    int** information = new int*[3];
    int count = 0;
    for (int i = 0; i < 3; ++i)
    {
        information[i] = new int[3];
    }
    for(int k=0;k<3;k++){
        for(int j=0;j<3;j++)
            information[k][j] = count++;
    }
    return information;
}

The file b.py:

import ctypes
from numpy.ctypeslib import ndpointer

lib = ctypes.CDLL('./library.so')
lib.ls1.restype = ndpointer(dtype=ctypes.c_int, shape=(3,))
res = lib.ls1()
print 'res of ls1:'
print res

lib.ls2.restype = ndpointer(dtype=ctypes.c_int, shape=(3,3))
res = lib.ls2()
print 'res of ls2:'
print res

I run the following commands:

g++ -c -fPIC *.cpp -o function.o
g++ -shared -Wl,-soname,library.so -o library.so function.o
python b.py

Then I get the following prints:

res of ls1:
[0 1 2]
res of ls2:
[[32370416        0 35329168]
 [       0 35329200        0]
 [     481        0 34748352]]

It seems I succeed in returning 1D array, just in the same way shown in the example. But I fail in returning 2D array. How can I get it work?
Thank you all for helping me!!!

Asked By: pfc

||

Answers:

You are allocating your array incorrectly.

int* may point to a beginning of a 1D attay.

int** never points to a beginning of a 2D array. It may point to a beginning of 1D array of pointers, each of which in turn point to a beginning of a 1D array. This is a legitimate data structure, but it’s different from a 2D array and is not compatible with Python’s

ndpointer(dtype=ctypes.c_int, shape=(3,3))

To return a real 2D array, you can do this:

typedef int a3[3];
a3 array[3] = new a3[3];
// no more allocations
for(int k=0;k<3;k++){ ...

Note that in a 2D array in C++ all dimensions except one are fixed.

If you want to return something that Python can interpret as a 2D array, you can return a 1D array:

int* array = new int[9];
// initialize it

Python will use it as a 3×3 matrix just fine. This allows you to vary all array dimensions: C++ never knows it’s a 2D array, you just multiply all the dimensions together.

If you for some reason do need an array of pointers (not recommended), you need to use something like this on the Python side:

int3type = ndpointer(dtype=ctypes.c_int, shape=(3,))
lib.ls2.restype = ndpointer(dtype=int3type, shape=(3,))

(I’m not a ctypes guru so take this with the grain of salt).

Finally, consider using boost::python. With it you can use std::vector on the C++ side normally, without resorting to low level hackery.

The previous answer did not work for me. Here is a solution that works

lib = ctypes.CDLL('./library.so')
lib.ls2.restype = ctypes.POINTER(ctypes.POINTER(ctypes.c_int * 3) * 3)
res = lib.ls2()

res_np = numpy.zeros((3,3))
for i in range(3):
    for j in range(3):
        res_np[i,j] = res.contents[i][0][j]

print(res_np)
Answered By: Damien
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.