2

So I'm trying to write a base class to handle classes that wrap std::vector, which I have working up to defining the __iter__ function. My first approach, and the one I wish to get working, is to have the begin() and end() functions in the base class. Doing this compiles fine, but when I run the code in python I get an error that looks simiar to:

Boost.Python.ArgumentError: Python argument types in

Container.__iter__(Container)

did not match C++ signature:

__iter__(boost::python::back_reference< stl_iter< std::vector< std::shared_ptr< K > > std::allocator< std::shared_ptr< K > > > >&>)

The following sample extension can be tested with

from test import *

c = Container()

for i in range(10):
    c.append(K(str(i)))

for i in c:
    print i

Sample extension:

#include <memory>
#include <vector>
#include <string>
#include <boost/python.hpp>

template<class T> 
T* get_pointer(std::shared_ptr<T> const& p) { return p.get(); }

template<class T>
class stl_iter {
protected:
    typedef typename T::value_type V;
    typedef typename T::iterator iter;
    virtual T& get_vector()=0;
public:
    virtual ~stl_iter() {}

    virtual void append(V item) {
        get_vector().push_back(item);
    }

    virtual iter begin() {
        return get_vector().begin();
    }

    virtual iter end() {
        return get_vector().end();
    }
};

class K {
    std::string val;
public:
    K(std::string s) : val(s) {}

    std::string get_val() const { return val; }
};
typedef std::shared_ptr<K> pK;
typedef std::vector<pK> vK;

class Container : public stl_iter<vK> {
    vK items;
protected:
    vK& get_vector() { return items; }

public:
    // Works if I uncomment these
    //vK::iterator begin() { return get_vector().begin(); }
    //vK::iterator end() { return get_vector().end(); }


public:
    virtual ~Container() {}
};
typedef std::shared_ptr<Container> pContainer;
typedef std::vector<pContainer> vContainer;

BOOST_PYTHON_MODULE_INIT(test) {
    using namespace boost::python;

    class_<K, pK>("K", init<std::string>())
        .def("__str__", &K::get_val)
        ;

    class_<Container, pContainer>("Container")
        .def("append", &Container::append)
        .def("__iter__", range(&Container::begin, &Container::end))
        ;
}
2
  • Do I understand correctly that the point of this is to expose vector types using boost python? And to do this in a general way? Commented Nov 21, 2011 at 19:40
  • I get this error trying to run a (non-c++11) adapted version of your code: TypeError: __init__() should return None, not 'NoneType' if I try to instantiate a Container. Commented Nov 27, 2011 at 20:52

2 Answers 2

1

The answer was actually quite simple: make the container behave like an stl container, and then to use boost's iterator<>() function instead of range().

To do this, I had to make the typedefs in stl_iter public, and to rename them to value_type and iterator.

Heres the updated C++ code.

#include <memory>
#include <vector>
#include <string>
#include <boost/python.hpp>

template<class T> 
T* get_pointer(std::shared_ptr<T> const& p) { return p.get(); }

template<class T>
class stl_iter {
protected:
    virtual T& get_vector()=0;
public:
    // Next two lines changed, and made public
    typedef typename T::value_type value_type;
    typedef typename T::iterator iterator;

    virtual ~stl_iter() {}

    virtual void append(value_type item) {
        get_vector().push_back(item);
    }

    virtual iterator begin() {
        return get_vector().begin();
    }

    virtual iterator end() {
        return get_vector().end();
    }
};

class K {
    std::string val;
public:
    K(std::string s) : val(s) {}

    std::string get_val() const { return val; }
};
typedef std::shared_ptr<K> pK;
typedef std::vector<pK> vK;

class Container : public stl_iter<vK> {
    vK items;
protected:
    vK& get_vector() { return items; }

public:
    virtual ~Container() {}
};
typedef std::shared_ptr<Container> pContainer;
typedef std::vector<pContainer> vContainer;

BOOST_PYTHON_MODULE_INIT(test) {
    using namespace boost::python;

    class_<K, pK>("K", init<std::string>())
        .def("__str__", &K::get_val)
        ;

    class_<Container, pContainer>("Container")
        .def("append", &Container::append)
        // Use iterator() instead of range()
        .def("__iter__", iterator<Container>())
        ;
}
Sign up to request clarification or add additional context in comments.

Comments

1

range() also can be used fine, but underlying iterator anyway should support STL semantic, here is sample:

...
.def("__iter__"
     , range<return_value_policy<copy_non_const_reference> >(
           &my_sequence<heavy>::begin
         , &my_sequence<heavy>::end))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.