...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
In C++, and STL in particular, we see iterators everywhere. Python also has iterators, but these are two very different beasts.
C++ iterators:
Python Iterators:
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:
Here, start/finish may be one of:
iterator
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])