The easiest solution is to implement __getitem__ and throw an IndexError exception for the invalid index.
I have compiled an example of this using %extend and %exception in SWIG to implement __getitem__ and raise an exception accordingly:
%module test %include "exception.i" %{
I tested it by adding to test.h:
static MyStruct *test() { static MyStruct inst = {0,0}; if (!inst.clientdata) { inst.len = 10; inst.clientdata = malloc(sizeof(double)*inst.len); for (size_t i = 0; i < inst.len; ++i) { inst.clientdata[i] = i; } } return &inst; }
And run the following Python:
import test for i in test.test(): print i
What prints:
python run.py 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
and then ends.
An alternative approach is also possible using a type map to map MyStruct to PyList :
%module test %{ #include "test.h" %} %typemap(out) (MyStruct *) { PyObject *list = PyList_New($1->len); for (size_t i = 0; i < $1->len; ++i) { PyList_SetItem(list, i, PyFloat_FromDouble($1->clientdata[i])); } $result = list; } %include "test.h"
This will create a PyList with a return value from any function that returns MyStruct * . I tested this %typemap(out) with the same function as the previous method.
You can also write the corresponding %typemap(in) and %typemap(freearg) for the reverse, something like this unverified code:
%typemap(in) (MyStruct *) { if (!PyList_Check($input)) { SWIG_exception(SWIG_TypeError, "Expecting a PyList"); return NULL; } MyStruct *tmp = malloc(sizeof(MyStruct)); tmp->len = PyList_Size($input); tmp->clientdata = malloc(sizeof(double) * tmp->len); for (size_t i = 0; i < tmp->len; ++i) { tmp->clientdata[i] = PyFloat_AsDouble(PyList_GetItem($input, i)); if (PyErr_Occured()) { free(tmp->clientdata); free(tmp); SWIG_exception(SWIG_TypeError, "Expecting a double"); return NULL; } } $1 = tmp; } %typemap(freearg) (MyStruct *) { free($1->clientdata); free($1); }
Using an iterator will make more sense for containers like linked lists, but for completeness you can do this for MyStruct with __iter__ . The key bit is that you get SWIG for another type of transfer for you, which provides __iter__() and next() necessary, in this case MyStructIter , which is determined and MyStructIter at the same time using %inline , since it does not is part of the regular C API:
%module test %include "exception.i" %{ #include <assert.h> #include "test.h" static int myErr = 0; %} %exception MyStructIter::next { assert(!myErr); $action if (myErr) { myErr = 0; // clear flag for next time PyErr_SetString(PyExc_StopIteration, "End of iterator"); return NULL; } } %inline %{ struct MyStructIter { double *ptr; size_t len; }; %} %include "test.h" %extend MyStructIter { struct MyStructIter *__iter__() { return $self; } double next() { if ($self->len--) { return *$self->ptr++; } myErr = 1; return 0; } } %extend MyStruct { struct MyStructIter __iter__() { struct MyStructIter ret = { $self->clientdata, $self->len }; return ret; } }
The requirements for iterating over containers are such that the container needs to implement __iter__() and return a new iterator, but in addition to next() , which returns the next element and increments the iterator, the iterator itself must also provide the __iter__() method. This means that either the container or the iterator can be used the same way.
MyStructIter should track the current state of the iteration - where are we and how much is left. In this example, I did this by pointing to the next element and the counter we use to indicate when we got to the end. You could also keep track of the state by storing a pointer to MyStruct , which uses an iterator, and a counter for the position inside that, something like:
%inline %{ struct MyStructIter { MyStruct *list; size_t pos; }; %} %include "test.h" %extend MyStructIter { struct MyStructIter *__iter__() { return $self; } double next() { if ($self->pos < $self->list->len) { return $self->list->clientdata[$self->pos++]; } myErr = 1; return 0; } } %extend MyStruct { struct MyStructIter __iter__() { struct MyStructIter ret = { $self, 0 }; return ret; } }
(In this case, we could just use the container itself as an iterator as an iterator by providing __iter__() , which returned a copy of the container and next() similar to the first type. T do this in my original answer, because I thought it would be less clear than two different types - container and iterator for this container)