9

I know, there are often many ways to solve certain problems. But here I know which way I want to have it, but I am unable to make it work with Python and SWIG...

I have a C-function, which returns me an array of double values:

double *my(int x)
{
   double a,b,*buf;
   buf = malloc (x * sizeof(double));
   a=3.14;
   b=2.7;
   buf[0]=a;
   buf[1]=b;
   return buf;
}

Here, I definitively want to have the array as a return value. Not, as in many examples a 'void' function, which writes into an input array. Now, I would like to get a SWIG-python wrapper, which might be used as:

>>> import example
>>> print example.my(7)
[3.14,2.7]

Whatever I do, I have some conceptual problems here - I always get s.th. like <Swig Object of type 'double *' at 0xFABCABA12>

I tried to define some typemaps in my swg file:

%typemap(out) double [ANY] {
  int i;
  $result = PyList_New($1_dim0);
  for (i = 0; i < $1_dim0; i++) {
    PyObject *o = PyFloat_FromDouble((double) $1[i]);
    PyList_SetItem($result,i,o);
  }
}

But still I am unable to get out my results as required. Does anyone have a simple code-example to achieve this task?

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
user701370
  • 91
  • 1
  • 1
  • 3

5 Answers5

4

The first problem is that your typemap doesn't match, you'll need a %typemap(out) double * { ... } since your function returns a pointer to double and not a double array.

If your list is of fixed size (i.e. an integer literal) as in the example you gave (which I assume is not what you want) you could simply change the typemap as I gave above and exchange $1_dim0 for the fixed size.

Otherwise your problem is that your %typemap(out) double * cannot possibly know the value of your parameter int x. You could return a struct that carries both the pointer and the size. Then you can easily define a typemap to turn that into a list (or a NumPy array, see also my response to Wrap C struct with array member for access in python: SWIG? cython? ctypes?).

Incidentally it's not possible to return a fixed sized array in C (see also this answer: Declaring a C function to return an array), so a %typemap(out) double [ANY] { ... } can never match.

Community
  • 1
  • 1
kynan
  • 13,235
  • 6
  • 79
  • 81
3

I suffered similar problem and solved it in following way.

// example.i

%module example

%include "carrays.i"
%array_class(float, floatArray);

float * FloatArray(int N);

float SumFloats(float * f);

 

# ipython

> a = example.floatArray(23) # array generated by swig's class constructor

> a

<example.floatArray; proxy of <Swig Object of type 'floatArray *' at 0x2e74180> >

> a[0]

-2.6762280573445764e-37 # unfortunately it is created uninitialized..

> b = example.FloatArray(23) # array generated by function

> b

<Swig Object of type 'float *' at 0x2e6ad80>

> b[0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
# .....
TypeError: 'SwigPyObject' object is not subscriptable

> #But there is a way to access b!!

> p = example.floatArray_frompointer(b) # i found this function by example. and twice tab

> p

<example.floatArray; proxy of <Swig Object of type 'floatArray *' at 0x2e66750> >

> p[0]

0.0

> p[0] = 42

> p[0]

42.0

Fortunately, all of these types (float *, floatArray *, and proxy of floatArray *) may be successfully passed to C++ function (such as SumFloats).

1

If you don't mind pulling in the numpy python module in your python code, you can do the following:

In the SWIG interface file:

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

%apply(float ARGOUT_ARRAY1[ANY]) {(float outarray1d[9])};
void rf(float outarray1d[9]);

Only the last two lines are specific to this example, the first stuff is default for numpy.i (see the numpy.i documentation elsewhere: http://docs.scipy.org/doc/numpy/reference/swig.interface-file.html).

In the C file (can also be inlined in .i file):

void rf(float outarray1d[9]) {
    float _internal_rf[9];
    /* ... */
    memcpy(outarray1d, _internal_rf, 9*sizeof(float));
}

Then you have a function which you can call from python as

import mymodule
a = mymodule.rf()
# a is a numpy array of float32's, with len 9

Now, if you don't want to be forced to pull in the numpy module in your python project, then I suggest you check numpy.i to see how they do the %typemap trick -- as I understand it, it's done with SWIG typemaps and not inherently tied to numpy - should be possible to do the same trick with tuples or lists as return value.

harm
  • 11
  • 1
1

You might want to look at the documentation around carray.i:

%include "carrays.i" %array_class(int, intArray);

http://www.swig.org/Doc2.0/Python.html#Python_nn48

Dion
  • 11
  • 1
  • 2
    I don't see how these could work for output (the input case is pretty clear from the documentation). Could you give an example? – kynan May 30 '11 at 19:35
-4

I don't know how much C you know - so apologies if I'm teaching you to suck eggs here...

There is no Array class in plain-ole C. An array is always a pointer to a piece of memory, not a "thing" in itself and therefore cannot just be printed-out by itself.

In this case - your "buf" is of type "double *". AFAICRem, if you want to print out the actual values stored at "the memory pointed-to by buf" you have to deallocate each one eg (in pseudocode): for i = 0 to buflength print buf[i]

Taryn East
  • 27,486
  • 9
  • 86
  • 108
  • 1
    Wrong: read section 6 of the [c-faq](http://c-faq.com/). Arrays are not pointers; pointers are not arrays. – pmg May 25 '11 at 21:26
  • Yes, they are not identical... that was not my point. The point is that in c, you can't do, say "my_array.length" In C, an array does not know how big it is (which in many OO languages it does), and you can't sensibly print it out in the way described above. You have to do the work yourself. – Taryn East May 26 '11 at 10:20