| Home | Libraries | People | FAQ | More

## Iterators

In C++, and STL in particular, we see iterators everywhere. Python also has iterators, but these are two very different beasts.

C++ iterators:

- C++ has 5 type categories (random-access, bidirectional, forward, input, output)
- There are 2 Operation categories: reposition, access
- A pair of iterators is needed to represent a (first/last) range.

Python Iterators:

- 1 category (forward)
- 1 operation category (next())
- Raises StopIteration exception at end

The typical Python iteration protocol: `for y in x...` is as follows:

    iter = x.__iter__()         # get iterator
    try:
        while 1:
        y = iter.next()         # get each item
        ...                     # process y
    except StopIteration: pass  # iterator exhausted

Boost.Python provides some mechanisms to make C++ iterators play along nicely as Python iterators. What we need to do is to produce appropriate `__iter__` function from C++ iterators that is compatible with the Python iteration protocol. For example:

    object get_iterator = iterator<vector<int> >();
    object iter = get_iterator(v);
    object first = iter.next();

Or for use in class_<>:

    .def("__iter__", iterator<vector<int> >())

range

We can create a Python savvy iterator using the range function:

- range(start, finish)
- range<Policies,Target>(start, finish)

Here, start/finish may be one of:

- member data pointers
- member function pointers
- adaptable function object (use Target parameter)

iterator

- iterator<T, Policies>()

Given a container `T`, iterator is a shortcut that simply calls `range` with &T::begin, &T::end.

Let's put this into action... Here's an example from some hypothetical bogon Particle accelerator code:

    f = Field()
    for x in f.pions:
        smash(x)
    for y in f.bogons:
        count(y)

Now, our C++ Wrapper:

    class_<F>("Field")
        .property("pions", range(&F::p_begin, &F::p_end))
        .property("bogons", range(&F::b_begin, &F::b_end));

stl_input_iterator

So far, we have seen how to expose C++ iterators and ranges to Python. Sometimes we wish to go the other way, though: we'd like to pass a Python sequence to an STL algorithm or use it to initialize an STL container. We need to make a Python iterator look like an STL iterator. For that, we use `stl_input_iterator<>`. Consider how we might implement a function that exposes `std::list<int>::assign()` to Python:

    template<typename T>
    void list_assign(std::list<T>& l, object o) {
        // Turn a Python sequence into an STL input range
        stl_input_iterator<T> begin(o), end;
        l.assign(begin, end);
    }

    // Part of the wrapper for list<int>
    class_<std::list<int> >("list_int")
        .def("assign", &list_assign<int>)
        // ...
        ;

Now in Python, we can assign any integer sequence to `list_int` objects:

    x = list_int();
    x.assign([1,2,3,4,5])