Reputation: 1034
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))
;
}
Upvotes: 2
Views: 775
Reputation: 1451
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))
Upvotes: 1
Reputation: 1034
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>())
;
}
Upvotes: 1