Differences between revisions 1 and 11 (spanning 10 versions)
Revision 1 as of 2003-01-21 13:49:50
Size: 5707
Editor: MikeRovner
Comment:
Revision 11 as of 2006-12-28 21:47:04
Size: 6579
Editor: ifp-30
Comment: Spelling fix
Deletions are marked like this. Additions are marked like this.
Line 5: Line 5:
But to be fair, a fully featured container wrapper is a lot of code and will take very long to compile.
It is a major research project to develop "a best" fully featured container
wrapper. Note that what you want depends on the element
type (e.g. what makes sense for std::complex<> as an element type does not
necessarily make sense for double, what makes sense for double might not make
sense for int and vice versa, etc. etc.)! I cannot even imagine what a
comprehensive solution for std::map<> could look like since it will have to
deal with combinations of two types.
To see an example of a more-or-less fully featured multi-dimensional array wrapper look
at [http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/cctbx/scitbx/include/scitbx/array_family/boost_python/flex_wrapper.h?rev=HEAD&content-type=text/vnd.viewcvs-markup flex_wrapper.h]
Line 16: Line 27:
We have three choises:
  a. wrap them with class_<> ourself,
  a. use scitbx XXX.h to wrap them
or
  a. write to_pyton_converter() and XXX_from python.
We have two choices:
  a. wrap them with ''class_<>'' ourself or
  a. write ''to_python_converter()'' and some wrappers to ''extract<data>'' from python.
Line 29: Line 39:
Approach (b) doesn't work out of the box for two reasons:
 * it doesn't include map,
 * it's too complex (for me) to maintain and adapt.
Approach (c) {{{
Approach (b) {{{
Line 37: Line 44:
but doesn't work. but doesn't work (for me :)).
Line 73: Line 80:
    static void add(T const& x, V const& v)
    {
        x.push_back(v);
    }
Line 74: Line 85:
void IndexError() { PyErr_; } void IndexError() { PyErr_SetString(PyExc_IndexError, "Index out of range"); }
Line 78: Line 89:
Than in BOOST_PYTHON_MODULE we use it: Then in BOOST_PYTHON_MODULE we use it:
Line 83: Line 94:
  .def("append", &Geometry::push_back   .def("append", &std_item<Geometry>::add,
Line 92: Line 103:
Line 135: Line 147:
we need to implement __contains__: we need to implement `__contains__`:
Line 179: Line 191:
but construct appropriate list ourselfs. but construct appropriate lists ourselfs.

It's a pity that BPL doesn't wrap STL containers out of the box. It would be good to have "batteries included". But to be fair, a fully featured container wrapper is a lot of code and will take very long to compile. It is a major research project to develop "a best" fully featured container wrapper. Note that what you want depends on the element type (e.g. what makes sense for std::complex<> as an element type does not necessarily make sense for double, what makes sense for double might not make sense for int and vice versa, etc. etc.)! I cannot even imagine what a comprehensive solution for std::map<> could look like since it will have to deal with combinations of two types. To see an example of a more-or-less fully featured multi-dimensional array wrapper look at [http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/cctbx/scitbx/include/scitbx/array_family/boost_python/flex_wrapper.h?rev=HEAD&content-type=text/vnd.viewcvs-markup flex_wrapper.h]

TableOfContents

C++ classes

In our case we have vector and map to wrap and had to decide how to wrap them.

        using namespace std;
        class Shape;
        typedef vector<Shape> Geometry;
        typedef map<string,Geometry> Layer;

We have two choices:

  1. wrap them with class_<> ourself or

  2. write to_python_converter() and some wrappers to extract<data> from python.

Our goal is to get to the working prototype as soon as we can. So in simplistic (a) approach

        using namespace boost::python;
        class_<Shape>("Shape");
        class_<Geometry>("Geometry");
        class_<Layer>("Layer");

We'll have our containers exposed but without any working machinery inside them. Approach (b)

        class vector_adapter

seems to be easiest to get the C++ containers exposed as Python containers

but doesn't work (for me :)).

So we have to revert to approach (a) and write a wrapper to add Python container machinery to the exposed classes.

list

To pretend a Python list a class shall have methods:

  • len,

  • getitem to be readable,

  • setitem to be writable,

  • delitem to delete elements.

We have len right avay:

          .def("__len__", &Geometry::size)

but for others let's have a helper class:

template<class T>
struct std_item
{
    typedef T::value_type V;
    static V& get(T const& x, int i)
    {
        if( i<0 ) i+=x.size();
        if( i>=0 && i<x.size() ) return x[i];
        IndexError();
    }
    static void set(T const& x, int i, V const& v)
    {
        if( i<0 ) i+=x.size();
        if( i>=0 && i<x.size() ) x[i]=v;
        else IndexError();
    }
    static void del(T const& x, int i)
    {
        if( i<0 ) i+=x.size();
        if( i>=0 && i<x.size() ) x.erase(i);
        else IndexError();
    }
    static void add(T const& x, V const& v)
    {
        x.push_back(v);
    }
};
void IndexError() { PyErr_SetString(PyExc_IndexError, "Index out of range"); }

which allows us to have nice pythonic negative indexes and easy to read definitions.

Then in BOOST_PYTHON_MODULE we use it:

class_<Geometry>("Geometry");
  .def("__len__", &Geometry::size)
  .def("clear", &Geometry::clear)
  .def("append", &std_item<Geometry>::add,
        with_custodian_and_ward<1,2>()) // to let container keep value
  .def("__getitem__", &std_item<Geometry>::get,
        return_value_policy<copy_non_const_reference>())
  .def("__setitem__", &std_item<Geometry>::set,
        with_custodian_and_ward<1,2>()) // to let container keep value
  .def("__delitem__", &std_item<Geometry>::del)
  ;

map

And the same approach for map.

template<class T>
struct map_item
{
    typedef T::key_type K;
    typedef T::mapped_type V;
    static V& get(T const& x, K const& i)
    {
        if( x.find(i) != x.end() ) return x[i];
        KeyError();
    }
    static void set(T const& x, K const& i, V const& v)
    {
        x[i]=v; // use map autocreation feature
    }
    static void del(T const& x, K const& i)
    {
        if( x.find(i) != x.end() ) x.erase(i);
        else KeyError();
    }
};
void KeyError() { PyErr_; }

And in our case:

class_<Layer>("Layer");
  .def("__len__", &Layer::size)
  .def("clear", &Layer::clear)
  .def("__getitem__", &map_item<Layer>::get,
        return_value_policy<copy_non_const_reference>())
  .def("__setitem__", &map_item<Layer>::set,
        with_custodian_and_ward<1,2>()) // to let container keep value
  .def("__delitem__", &map_item<Layer>::del)
  ;

But it's only very basic functionality. Let's add some convinience.

key in container

To use python construct key in container we need to implement __contains__:

  • for vector (list, queue)

    static bool in(T const& x, V const& v)
    {
            return find_eq(x.begin, x.end, v) != x.end();
    }
  • for map

    static bool in(T const& x, K const& i)
    {
            return x.find(i) != x.end();
    }

iterators

Also very useful thing is to iterate through our containers. BR For the vector ["boost.python"] has it:

.def("__iter__", iterator<Geomery>())

And for map it needs some help

static list keys(T const& x)
{
        list t;
        for(T::const_iterator it=x.begin; it!=x.end(); ++it)
          t.append(it->first);
        return t;
}
static list values(T const& x)
{
        list t;
        for(T::const_iterator it=x.begin; it!=x.end(); ++it)
          t.append(it->second);
        return t;
}
static list items(T const& x)
{
        list t;
        for(T::const_iterator it=x.begin; it!=x.end(); ++it)
          t.append(make_tuple(it->first,it->second));
        return t;
}

Here we used simplistic approach for the map. We didn't use iterator protocol, but construct appropriate lists ourselfs.

index

Some ice on top:

static int std_item::index(T const& x, V const& v)
{
        int i=0;
        for(T::const_iterator it=x.begin; it!=x.end(); ++it,++i)
          if( *it == v ) return i;
        return -1;
}

static int map_item::index(T const& x,  const& k)
{
        int i=0;
        for(T::const_iterator it=x.begin; it!=x.end(); ++it,++i)
          if( it->first == k ) return i;
        return -1;
}

and we are all set.

download

You can download container helper classes from attachment:container.h.

boost.python/StlContainers (last edited 2011-11-20 08:28:34 by d199-126-61-239)

Unable to edit the page? See the FrontPage for instructions.