DrJohn
DrJohn

Reputation: 86

SWIG+c+Python: Passing and receiving c arrays

I am trying to reuse some old c code with SWIG and Python. Right now I am quite confused. The errors I get can be demonstrated on a small example:

bsp.h:

extern void add(int a[], int b[], int c[]);

bsp.c:

#include "bsp.h" 
void add(int a[], int b[], int c[]) 
{ 
    c[0] = a[0] + b[0]; 
    c[1] = a[1] + b[1]; 
}

bsp.i

%module bsp
%{
    #include "bsp.h";
%}
%include "bsp.h";

setup.py:

#!/usr/bin/env python

from distutils.core import setup, Extension

bsp_module = Extension('_bsp',
    sources = ['bsp_wrap.c', 'bsp.c']
)

setup(name = 'bsp',
    ext_modules = [bsp_module],
    py_modules = ["bsp"]
)

The example Python file "pybsp.py":

import bsp

a = [1, 1]
b = [1, 1]
c = []

bsp.add(a, b, c)

print(c)

And I get the error:

Traceback (most recent call last):
    File "pybsp.py", line 31, in <module>
    bsp.add(a, b, c)
TypeError: in method 'add', argument 1 of type 'int []'

Now, why I am confused is that the SWIG Documentation says: "C/C++ pointers are fully supported by SWIG. Furthermore, SWIG has no problem working with incomplete type information."

I also tried to add

%apply int * INPUT { int *a}
%apply int * INPUT { int *b}
%apply int * OUTPUT { int *c}

to my .i file, which was recommended in this context, without success. My guess is, that I have to create a pointer like object in Python to pass, but I don't know how that works and also hope, that there is a simpler way.

Thank you very much for your help!

P.S.: As you might guess, this is my first contact with SWIG, so, unfortunately, I was not able to deduce the solution from solutions of seemingly similar problems.

EDIT: I found out that for arrays with given dimensions, as above, NumPy seems to be a good option to avoid the wrapping by hand. Basic examples are given here. Accordingly I changed my function definition to

void add(int* a, int dim_a, int *b, int dim_b, int *c, int dim_c)

Now the wrapper seems to have a chance to convert the NumPy array into a C array.

i-File

%module bsp
%{
    #define SWIG_FILE_WITH_INIT
    #include "bsp.h"
%}

%include "numpy.i"

%init %{
    import_array();
%}

%apply (int* IN_ARRAY1, int DIM1){(int* a, int dim_a), (int* b, int dim_b)}
%apply (int* ARGOUT_ARRAY1, int DIM1){(int* c, int dim_c)}

%include "bsp.h"

setup.py

#!/usr/bin/env python

from distutils.core import setup, Extension
import numpy

try:
        numpy_include = numpy.get_include()
except AttributeError:
        numpy_include = numpy.get_numpy_include()

bsp_module = Extension('_bsp',
                       sources=['bsp_wrap.c', 'bsp.c'],
                       include_dirs=[numpy_include]
                       )

setup(name='bsp',
      ext_modules=[bsp_module],
      py_modules=["_bsp"]
      )

And finally the python script, where I wanted to use int32 to avoid a type conversion Error (int64 -> int32) from NumPy

import bsp
import numpy as np
a = np.array([1, 1], dtype=np.int32)
b = np.array([1, 1], dtype=np.int32)
c = np.array([1, 1], dtype=np.int32)

bsp.add(a, b, c)

print(c)

Now I got rid of the previous Error, but I have a new one:

  File "pybsp.py", line 10, in <module>
    bsp.add(a, b, c)
TypeError: Int dimension expected.  'unknown type' given.

Any suggestions?

Upvotes: 3

Views: 2939

Answers (2)

DrJohn
DrJohn

Reputation: 86

Alright, now I have it. As written in the EDIT above, with numpy.i the arrays can be wrapped quite comfortably. What I did not see was, that the ARGOUT Array does not want an array as Input, as in C. There is just the dimension needed. So, with the Code above, the Script

import bsp
import numpy as np
a = np.array([1, 1], dtype=np.int32)
b = np.array([1, 1], dtype=np.int32)

c = bsp.add(a, b, np.shape(a)[0])

print(c)

Gives the desired Output

[2 2]

Upvotes: 2

Jacques de Hooge
Jacques de Hooge

Reputation: 7000

A Python list is quite different from a C array. In C the name of an array is a pointer to a contiguous block of memory containing its elements. Python lists are complicated datastructures where memory isn't contiguous and simple address calculations do not hold. So you cannot expect C code meant for C arrays to work with Python lists.

You can access Python lists from C as described here:

http://effbot.org/zone/python-capi-sequences.htm

Upvotes: 1

Related Questions