From 6ca6e82f7c766a0894327571fd1057a191198572 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 27 Apr 2016 14:33:52 +0200 Subject: [PATCH] fix various iterator issues (fixes #181) --- example/issues.cpp | 5 ++++ example/issues.py | 3 ++ example/issues.ref | 1 + include/pybind11/pytypes.h | 61 +++++++++++++++++++++++++++++++------- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/example/issues.cpp b/example/issues.cpp index c8af7569..1fa4d57e 100644 --- a/example/issues.cpp +++ b/example/issues.cpp @@ -57,4 +57,9 @@ void init_issues(py::module &m) { v.push_back(p4); return v; }); + + // #181: iterator passthrough did not compile + m2.def("iterator_passthrough", [](py::iterator s) -> py::iterator { + return py::make_iterator(std::begin(s), std::end(s)); + }); } diff --git a/example/issues.py b/example/issues.py index e0726f0b..be241e18 100644 --- a/example/issues.py +++ b/example/issues.py @@ -6,6 +6,7 @@ sys.path.append('.') from example.issues import print_cchar, print_char from example.issues import DispatchIssue, dispatch_issue_go from example.issues import Placeholder ,return_vec_of_reference_wrapper +from example.issues import iterator_passthrough print_cchar("const char *") print_char('c') @@ -29,3 +30,5 @@ b = PyClass2() dispatch_issue_go(b) print(return_vec_of_reference_wrapper(Placeholder(4))) + +print(list(iterator_passthrough(iter([3, 5, 7, 9, 11, 13, 15])))) diff --git a/example/issues.ref b/example/issues.ref index fce7b955..6d9a893c 100644 --- a/example/issues.ref +++ b/example/issues.ref @@ -3,3 +3,4 @@ c Failed as expected: Tried to call pure virtual function "dispatch" Yay.. [Placeholder[1], Placeholder[2], Placeholder[3], Placeholder[4]] +[3, 5, 7, 9, 11, 13, 15] diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 689585f8..a300dd5d 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -68,7 +68,7 @@ public: return handle(tmp); } - object& operator=(object &other) { + object& operator=(const object &other) { other.inc_ref(); dec_ref(); m_ptr = other.m_ptr; @@ -231,8 +231,8 @@ NAMESPACE_END(detail) Name(const handle &h, bool borrowed) : Parent(h, borrowed) { CvtStmt; } \ Name(const object& o): Parent(o) { CvtStmt; } \ Name(object&& o) noexcept : Parent(std::move(o)) { CvtStmt; } \ - Name& operator=(object&& o) noexcept { (void) static_cast(object::operator=(std::move(o))); CvtStmt; return *this; } \ - Name& operator=(object& o) { return static_cast(object::operator=(o)); CvtStmt; } \ + Name& operator=(object&& o) noexcept { (void) object::operator=(std::move(o)); CvtStmt; return *this; } \ + Name& operator=(const object& o) { return static_cast(object::operator=(o)); CvtStmt; } \ bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } #define PYBIND11_OBJECT(Name, Parent, CheckFun) \ @@ -244,21 +244,62 @@ NAMESPACE_END(detail) class iterator : public object { public: - PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) - iterator& operator++() { - if (ptr()) - value = object(PyIter_Next(m_ptr), false); + PYBIND11_OBJECT_CVT(iterator, object, PyIter_Check, value = object(); ready = false) + iterator() : object(), value(object()), ready(false) { } + iterator(const iterator& it) : object(it), value(it.value), ready(it.ready) { } + iterator(iterator&& it) : object(std::move(it)), value(std::move(it.value)), ready(it.ready) { } + + /** Caveat: this copy constructor does not (and cannot) clone the internal + state of the Python iterable */ + iterator &operator=(const iterator &it) { + (void) object::operator=(it); + value = it.value; + ready = it.ready; return *this; } + + iterator &operator=(iterator &&it) noexcept { + (void) object::operator=(std::move(it)); + value = std::move(it.value); + ready = it.ready; + return *this; + } + + iterator& operator++() { + if (m_ptr) + advance(); + return *this; + } + + /** Caveat: this postincrement operator does not (and cannot) clone the + internal state of the Python iterable. It should only be used to + retrieve the current iterate using operator*() */ + iterator operator++(int) { + iterator rv(*this); + rv.value = value; + if (m_ptr) + advance(); + return rv; + } + bool operator==(const iterator &it) const { return *it == **this; } bool operator!=(const iterator &it) const { return *it != **this; } + const handle &operator*() const { - if (m_ptr && !value) - value = object(PyIter_Next(m_ptr), false); + if (!ready && m_ptr) { + auto& self = const_cast(*this); + self.advance(); + self.ready = true; + } return value; } + private: - mutable object value; + void advance() { value = object(PyIter_Next(m_ptr), false); } + +private: + object value; + bool ready; }; class iterable : public object {