4

I have a C function I want to use in Python:

extern int convertAtoB( stateStruct *myStruct,
                        const double PointA[3],
                        double PointB[3]);

Using SWIG, I think I need to define a typemap to convert the two points (PointA the input, PointB the output) so that Python can use it. There doesn't seem to be a typemap in typemaps.i that works with this, so I have to define one. I can't seem to find examples of this for arrays in the SWIG documentation.

I would like to use this library like so:

s = externalStruct()
point_a = [1, 2, 3]
result, point_b = convertAtoB(s, point_a)
print point_b
"expect [4, 5, 6]"

How would I do this? Thanks

3 Answers 3

3

You're almost there. To get rid of the dummy argument in the python signature you need to change %typemap(in) for PointB[3] to %typemap(in,numinputs=0) to instruct SWIG to ignore that input value (you're already taking a copy of it anyway). This will remove the dummy argument from the python method signature.

I'm not sure however, if you need to copy the entire %typemap(in) for that specialization. Probably there's a way to reuse the actual typemap, but I don't know how. Otherwise you'll end up with an additional

%typemap(in,numinputs=0) double PointB[3] (double temp[$1_dim0]) {
  int i;
  if (!PySequence_Check($input)) {
    PyErr_SetString(PyExc_ValueError,"Expected a sequence");
    return NULL;
  }
  if (PySequence_Length($input) != $1_dim0) {
    PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected $1_dim0 elements");
    return NULL;
  }
  for (i = 0; i < $1_dim0; i++) {
    PyObject *o = PySequence_GetItem($input,i);
    if (PyNumber_Check(o)) {
      temp[i] = (double) PyFloat_AsDouble(o);
    } else {
      PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers");      
      return NULL;
    }
  }
  $1 = temp;
}
Sign up to request clarification or add additional context in comments.

Comments

2

Here's one solution I found, but it might not be the best:

%typemap(in) double[ANY] (double temp[$1_dim0]) {
  int i;
  if (!PySequence_Check($input)) {
    PyErr_SetString(PyExc_ValueError,"Expected a sequence");
    return NULL;
  }
  if (PySequence_Length($input) != $1_dim0) {
    PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected $1_dim0 elements");
    return NULL;
  }
  for (i = 0; i < $1_dim0; i++) {
    PyObject *o = PySequence_GetItem($input,i);
    if (PyNumber_Check(o)) {
      temp[i] = (double) PyFloat_AsDouble(o);
    } else {
      PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers");      
      return NULL;
    }
  }
  $1 = temp;
}

This is an example in the documents I finally came across which convert Python lists into arrays. The next part was harder, piecing together several examples I could convert the return array into a python list:

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

However, I have to create one of these for every return value in the API. Also I have to call it with a dummy value as a parameter:

point_b = convertAtoB(s, point_a, dummy)

Is there a better way?

Comments

1

This is an old thread, but I answer it because not so many post has been answered regarding SWIG.

To target specifically the situation above

%typemap(in, numinputs=0) double PointB[3] {
  double tmp[3];
  $1 = tmp;
}

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.