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<
boost::is_const<T>::value
>::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!