开发者

swig typemap for python: input and output arrays

开发者 https://www.devze.com 2023-01-14 23:10 出处:网络
I have a C function I want to use in Python: extern int convertAtoB( stateStruct *myStruct, const double PointA[3],

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_开发者_如何学Gob = convertAtoB(s, point_a)
print point_b
"expect [4, 5, 6]"

How would I do this? Thanks


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;
}


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?


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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消