jeudi 11 décembre 2008

boost::python iterators on non-standard contrainers

If you use C++, you have to love boost. For one thing, it helps you get away from C++ by offering a nice bridge to python :-) Boost has always great documentation, but from time to time, one or two extra samples would be of help. Here is one very small contribution.

You have a C++ container type that is accessible to python using boost::python. Of course, you want to iterate on all the elements from python code like this :

my_container_instance = make_a_container()
for x in my_container_instance:
print x

When we look at the documentation, we find that we need to introduce the binding for __iter__ like this :

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

And it works, if your container is a std::vector<int>

My container is a polygon, not a std::vector. It does not have a typedef for iterator, it has a typedef for Vertex_iterator. It has no begin() method, it has a vertices_begin() method. You could have a similar problem. If that's the case, you can expect a compilation error

c:\...\boost\python\iterator.hpp(50) : error C2039:'begin' :
n'est pas membre de 'Polygon'

Here is the offending code :

// Guts of template class iterators<>, below.
template <bool const_ = false>
struct iterators_impl
template <class T>
struct apply
typedef typename T::iterator iterator;
static iterator begin(T& x) { return x.begin(); }
static iterator end(T& x) { return x.end(); }

And it's called by that :

// An "ordinary function generator" which contains static begin(x) and
// end(x) functions that invoke T::begin() and T::end(), respectively.
template <class T>
struct iterators
: detail::iterators_impl<
>::template apply<T>

That's not hard to fix, and there is nothing to modify in boost::python. What a nice illustration of the Open Closed Principle.

The only thing to do is to specialize the iterators template to provide our own begin/end functions. Somewhere in the .cpp file before you added the __iter__ declaration, add something like this code :

namespace boost
namespace python
template <>
struct iterators< Polygon >
typedef Polygon::Vertex_iterator iterator;
static iterator begin( Polygon& x) { return x.vertices_begin(); }
static iterator end( Polygon& x) { return x.vertices_end(); }

And have fun :-)

1 commentaire:

UncleZeiv a dit...

Very useful, thanks!