1
votes

I have a c++ implementation wrapped with SWIG and compiled to a module which can be used by python.

I am using ctypes to call the function with ctype arguments, int double etc. The output of my_function(ctype args) is an int**, i.e. it's a multidimensional array.

How can I cast this into a 2D numpy array inside the python script? I have been looking at ctypes pointers but so far I have had no luck. I have spent many, many hours reading the C-API of python and numpy for use with SWIG, and implementing on the c++ side to return a numpy array has so far been incredibly hard and completely unsuccessful.

2
Looks like a duplicate. What about this answer? stackoverflow.com/questions/22425921/… - The Quantum Physicist
That question seems to be the dual to this one, @TheQuantumPhysicist (numpy -> int**, not int** -> numpy) - Eric
Is the int** dynamically allocated? Whose job is it to delete [] it? - Eric
"int**, i.e. it's a multidimensional array" - non sequitur. A pointer is not an array. It can point to an array, but it can also point to a single item or to nothing at all. int i; int* ip = &i; int** ipp = &ip; - Pete Becker

2 Answers

0
votes

I don't think this can be done on the Python side; it will have to be done within the C/C++ layer using NumPy's C-API interface (PyArray_SimpleNewFromData is the relevant function – see this answer for some details). Here is an example of this in a Cython script.

Note that handling of deallocation is complicated in this case: as far as I know, there's no mechanism to allow numpy to handle it automatically. You'll just have to make sure that whatever script deallocates the array doesn't do so when the numpy wrapper is still being used.

Edit: if your int** does not point to a contiguous memory block, I don't believe this will work. NumPy can only (easily) handle contiguous data buffers.

0
votes

Using NumPy and numpy.i, this is quite easy

Interface header

#pragma once
void fun(int** outArray, int* nRows, int* nCols);

Implementation

#include "test.h"
#include <malloc.h>
void fun(int** outArray, int* nRows, int* nCols) {
  int _nRows = 100;
  int _nCols = 150;
  int* _outArray = (int*)malloc(sizeof(int)*_nRows*_nCols);
  *outArray = _outArray;
  *nRows = _nRows;
  *nCols = _nCols;
}

SWIG interface header

%module example
%{
  #define SWIG_FILE_WITH_INIT
  #include "test.h"
%}

%include "numpy.i"

%init
%{
  import_array();
%}

%apply (int** ARGOUTVIEWM_ARRAY2, int* DIM1, int* DIM2) {(int** outArray, int* nRows, int* nCols)}
%include "test.h"

The typemap ARGOUTVIEWM_ARRAY2 creates a managed NumPy array and free is automatically called, when the NumPy object is destroyed in Python.

If you want to create the wrapper yourself using the Python C API, you can look into the generated code made by SWIG using numpy.i