Home > Software engineering >  How can you wrap a C function with SWIG that takes in a Python numpy array as input without explic
How can you wrap a C function with SWIG that takes in a Python numpy array as input without explic

Time:06-23

I have a library of C classes that I am building a Python interface for using SWIG. Many of these classes have methods that take in a double* array or int* array parameter without inputting a size. For example, there are many methods that have a declaration like one of the following:

void func(double* array);

void func2(double* array, double unrelated_parameter, ...);

I would like to be able to use these functions in Python, with the user passing in a Python numpy array. The size of these arrays are never given as a parameter to the function. The size of the input array is given in the constructor of the objects of these C classes and it is assumed that every input array that is given as a parameter to these class methods will have the same size. All of the numpy examples I have seen require me to add an int array_size parameter to the C method/function being wrapped.

Is there a way to wrap these C functions without having change the API of my entire C library to include an int array_size parameter for every single function? Ideally, a user should pass in a Python numpy array and SWIG will automatically convert it to a double or int array on the C side.

I have already included numpy.i and followed the instructions here: https://numpy.org/doc/stable/reference/swig.interface-file.html but am getting errors like the following:

TypeError: in method 'func', argument 2 of type 'double *'

CodePudding user response:

One way I can think of is to suppress the "no size" version of the function and extend the class to have a version with a throw-away dimension variable that uses the actual parameter in the class.

Example:

test.i

%module test

%{
#define SWIG_FILE_WITH_INIT

class Test {
public:
    int _dim; // needs to be public, or have a public accessor.
    Test(int dim) : _dim(dim) {}
    double func(double* array) {
        double sum = 0.0;
        for(int i = 0; i < _dim;   i)
            sum  = array[i];
        return sum;
    }
};
%}

%include "numpy.i"
%init %{
import_array();
%}

%apply (double* IN_ARRAY1, int DIM1) {(double* array, int /*unused*/)};

%ignore Test::func; // so the one-parameter version isn't wrapped

class Test {
public:
    Test(int dim);
    double func(double* array);
};

%rename("%s") Test::func; // unignore so the two-parameter version will be used.

%extend Test {
    double func(double* array, int /*unused*/) {
        return $self->func(array);
    }
}

Demo:

>>> import test
>>> t = test.Test(5)
>>> import numpy as np
>>> a = np.array([1.5,2.0,2.5,3.75,4.25])
>>> t.func(a)
14.0
  • Related