Bluegreen17
Bluegreen17

Reputation: 993

Is there a way to pass different array types from python to c++ using SWIG?

I want to pass a c++ function into python using SWIG that inputs a double array AND an integer array. Is there a way to do this?

For example, I have a c++ function that takes in a double and an int array:

double myfun(double* a, int n, int* b, int m){...}

In the SWIG interface file I tried writing

%apply (double* IN_ARRAY1, int DIM1, int* IN_ARRAY1, int DIM1) {(double* a, int n, int* b, int m)}

but no luck. It compiled, but I could not call the myfun function in python like

myfun(a,b) 

where a is a double numpy array and b an integer numpy array. I get the following error in python:

myfun() takes exactly 4 arguments (2 given)

Any suggestions? Is this even possible?

Thanks!

Upvotes: 3

Views: 506

Answers (1)

The short answer is that you need to use a typemap that takes two inputs, but has the numinputs attribute set to 1, e.g.:

%module test

%typemap(in,numinputs=1) (double *a, size_t) %{
  assert(PyList_Check($input));
  $2 = PyList_Size($input);
  $1 = malloc(sizeof *$1 * $2);
  for(size_t i = 0; i < $2; ++i) {
    $1[i] = PyFloat_AsDouble(PyList_GetItem($input, i));
  }
%}

%typemap(in, numinputs=1) (int *b, size_t) %{
  assert(PyList_Check($input));
  $2 = PyList_Size($input);
  $1 = malloc(sizeof *$1 * $2);
  for (size_t i = 0; i < $2; ++i) {
    $1[i] = PyInt_AsLong(PyList_GetItem($input, i));
  }
%}

%typemap(freearg) (double *a, size_t) %{
  free($1);
%}

%typemap(freearg) (int *b, size_t) %{
  free($1);
%}

%inline %{
  double myfun(double *a, size_t n, int *b, size_t m) {
    (void)a; (void) b;
    printf("%d, %d\n", n, m);
    for (size_t i = 0; i < (n > m ? n : m); ++i) {
      printf("%d: %f - %d\n", i, i < n ? a[i] : 0, i < m ? b[i] : 0);
    }
    return 1.0;
  }
%}

This works, there's a typemap for each pair of (array, len) and is sufficient to be used as:

import test

a = [0.5, 1.0, 1.5]
b = [1,2,3]

test.myfun(a,b)

We could have used alloca or C99's VLA feature to avoid the need for the malloc call, but for illustration purposes this works.

(Note: you've not written const anywhere in the function prototype, but the implication is that it's not modifying the input arrays. If that's not the case then you'd need to write a corresponding argout typemap to copy from the allocated array, back into the Python list).

It is also quite repetitive however, so it might be nicer if we could share some code between the two - you can do that using some more advanced SWIG features if needed. You could also add support for memory views as input instead of just lists if needed.

Upvotes: 1

Related Questions