11

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

Adam
  • 463
  • 2
  • 6
  • 14

3 Answers3

14

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.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • 1
    Tried this with SWIG, works great. Thanks! Since there's 3 replies all saying I should use ctypes though I'll prolly switch to that in the near future. – Adam Apr 27 '11 at 04:40
  • No need to code the typemaps by hand if you use `numpy.i`, see my answer. – kynan May 30 '11 at 19:21
8

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)
kynan
  • 13,235
  • 6
  • 79
  • 81
2

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)

Here's what I tested with:

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
samplebias
  • 37,113
  • 6
  • 107
  • 103
  • Same error as before...arr is a: <__main__.c_int_Array_8 object at 0x7fdb9da9ad40> not a 'int *' – Adam Apr 25 '11 at 16:44
  • so I tried switching to ctypes from SWIG, and it'll import the .so and python doesn't complain about passing the parameters, but its returning the wrong value from the function. I tried this on another function and there was the same result (incorrect output). – Adam Apr 27 '11 at 04:28
  • scratch that, the problem is with my C program apparently for the cosetCoding function. So this works! yay! thanks! My function that is returning a double is still returning garbage though (strangely enough). – Adam Apr 27 '11 at 04:39
  • @Adam great to hear you're making progress! – samplebias Apr 27 '11 at 11:23