Reputation: 483
I am trying to access a C function with the following prototype from python using swig:
int cosetCoding(int writtenDataIn, int newData, const int memoryCells, int *cellFailure, int failedCell);
Swig creates the .so with no problems and I can import it into python, but when I try to access it with the following:
cosetCoding.cosetCoding(10,11,8,[0,0,0,0,0,0,0,0],0)
I get the following traceback:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: in method 'cosetCoding', argument 4 of type 'int *'
The pointer is supposed to be an int array with size defined by memoryCells
Upvotes: 11
Views: 10876
Reputation: 13653
Mark is right, you'll need a typemaps. However, there's no need to code the typemap by hand if you use numpy.i
(http://docs.scipy.org/doc/numpy/reference/swig.interface-file.html), which already defines the necessary typemaps to turn C into NumPy arrays and vice versa.
In your case (assuming cellFailure
is an input array) you'll want to use
%apply (int DIM1, int* IN_ARRAY1) {(int memoryCells, int *cellFailure)}
Note (as Mark already pointed out) that this conveniently fuses these 2 parameters in C to a single python array parameter, no need to pass the array length separately. Your call will look like:
from numpy import asarray
cosetCoding.cosetCoding(10,11,asarray([0,0,0,0,0,0,0,0]),0)
Upvotes: 8
Reputation: 178399
Use ctypes if you can. It is simpler. However, since you asked for SWIG, what you need is a typemap describing how to handle the int*. SWIG doesn't know how many integers may be pointed to. Below is hacked from an example in the SWIG documentation on multi-argument typemaps:
%typemap(in) (const int memoryCells, int *cellFailure) {
int i;
if (!PyList_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expecting a list");
return NULL;
}
$1 = PyList_Size($input);
$2 = (int *) malloc(($1)*sizeof(int));
for (i = 0; i < $1; i++) {
PyObject *s = PyList_GetItem($input,i);
if (!PyInt_Check(s)) {
free($2);
PyErr_SetString(PyExc_ValueError, "List items must be integers");
return NULL;
}
$2[i] = PyInt_AsLong(s);
}
}
%typemap(freearg) (const int memoryCells, int *cellFailure) {
if ($2) free($2);
}
Note that with this definition, when called from Python leave out the memoryCells
parameter and just pass an array such as [1,2,3,4]
for cellFailure
. The typemap will generate the memoryCells
parameter.
P.S. I can post a fully working example (for Windows) if you want it.
Upvotes: 14
Reputation: 37929
You need to construct an array of c_int
for that to work:
arr = (ctypes.c_int * 8)(0, 0, 0, 0, 0, 0, 0, 0)
cosetCoding.cosetCoding(10, 11, 8, arr, 0)
Updated Adding a more complete example. I'm using ctypes version 1.1.0 under Python 2.6; perhaps we're doing something slightly differently?
Maybe pass ctypes.byref(arr)
instead?
cosetCoding.cosetCoding(10, 11, 8, ctypes.byref(arr), 0)
Shared object: cosetCoding.c
#include <stdio.h>
int cosetCoding(int writtenDataIn, int newData, const int memoryCells, int *cellFailure, int failedCell)
{
printf("cellFailure: %d %d\n", cellFailure[0], cellFailure[1]);
}
Compile:
% gcc -shared -fPIC -o cosetCoding.so cosetCoding.c
Python script: test_coset.py
import ctypes
cosetCoding = ctypes.cdll.LoadLibrary('./cosetCoding.so')
arr = (ctypes.c_int * 8)(1, 2, 3, 4, 5, 6, 7, 8)
cosetCoding.cosetCoding(10, 11, 8, arr, 0)
Output:
% python test_coset.py
cellFailure: 1 2
Upvotes: 2