Search code examples
pythonctypeerrorswig

SWIG TypeError: in method 'my_method', argument 1 of type 'double const []'


I am trying to wrap a c function into python using swig.

The function, defined in my_method.h and implemented in my_method.c, is:

double my_method(const double a[], const double b[], const long int len_a, const long int len_b){
...
}

my swig interface file (my_method.i) is:

/* file : my_method.i */

/* name of the python module*/
%module my_method
%{
    #include "my_method.h"
%}

%include "my_method.h"

I use dist-utils to generate the wrapped function

#!/usr/bin/env python

"""
setup.py file for SWIG wrapping
"""

from distutils.core import setup, Extension

my_method_module = Extension('_my_method',
                           sources=['my_method.c', 'my_method_wrap.c'],
                           )

setup (name = 'my_method',
       version = '0.1',
       author      = "author",
       description = """my method""",
       ext_modules = [my_method_module],
       py_modules = ["my_method"],
       )

and then

swig -python my_method.i
python setup.py build_ext --inplace

however when I run my main script in python:

a = np.ones(4)
b =  np.ones(4)
res = my_method(a, b, len(a), len(b))

I get the following error:

    return _my_method.my_method(a, b, len_a, len_b)
TypeError: in method 'my_method', argument 1 of type 'double const []'

any suggestion?


Solution

  • SWIG has no idea that double a[] represents one or many doubles or that len_a indicates the length. NumPy has a SWIG interface file that defines the typemaps needed to associate a pointer and size with a numpy array or Python list called numpy.i (docs) (download).

    Here's a standalone example. You'll either need to reorder the parameters so pointer/size are together or add an adapter function. I've demonstrated the latter in case reordering is not possible:

    %module test
    
    %{
    #define SWIG_FILE_WITH_INIT
    
    // sample implementation of OP function.
    // Just sum all the values in both arrays.
    double my_method(const double a[], const double b[], const long int len_a, const long int len_b) {
        double sum = 0.0;
        for(long int aa = 0; aa < len_a; ++aa)
            sum += a[aa];
        for(long int bb = 0; bb < len_b; ++bb)
            sum += b[bb];
        return sum;
    }
    
    // adapter function in case re-ordering parameters is not possible
    double my_method2(const double a[], int len_a, const double b[], int len_b) {
        return my_method(a, b, len_a, len_b);
    }
    %}
    
    %include "numpy.i"
    
    %init %{
    import_array();
    %}
    
    // apply the typemaps for numpy arrays to the two pairs of parameters representing the arrays
    %apply (double* IN_ARRAY1, int DIM1) {(double* a, int len_a)};
    %apply (double* IN_ARRAY1, int DIM1) {(double* b, int len_b)};
    
    // process the adapter function and rename
    %rename(my_method) my_method2;
    double my_method2(double* a, int len_a, double* b, int len_b);
    

    I built using MSVC on Windows. Note that include paths are needed to Python and its NumPy package headers:

    swig -python test.i
    cl /LD /W3 /Ic:\python310\include /Ic:\Python310\Lib\site-packages\numpy\core\include /Fe_test.pyd test_wrap.c -link /libpath:c:\python310\libs
    

    Demo:

    >>> import test
    >>> test.my_method([1, 2, 3], [4, 5, 6])
    21.0
    >>> import numpy as np
    >>> a = np.ones(4)
    >>> b = np.ones(4)
    >>> test.my_method(a, b)
    8.0