Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 3 additions & 22 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -1457,29 +1457,10 @@ template <typename Type> class enum_ : public class_<Type> {

/// Export enumeration entries into the parent scope
enum_ &export_values() {
#if !defined(PYPY_VERSION)
PyObject *dict = ((PyTypeObject *) this->m_ptr)->tp_dict;
PyObject *key, *value;
ssize_t pos = 0;

while (PyDict_Next(dict, &pos, &key, &value)) {
if (PyObject_IsInstance(value, this->m_ptr))
m_parent.attr(key) = value;
for (auto item : reinterpret_borrow<dict>(((PyTypeObject *) this->m_ptr)->tp_dict)) {
if (isinstance(item.second, this->m_ptr))
m_parent.attr(item.first) = item.second;
}
#else
/* PyPy's cpyext still has difficulties with the above
CPython API calls; emulate using Python code. */
dict d; d["t"] = *this; d["p"] = m_parent;
PyObject *result = PyRun_String(
"for k, v in t.__dict__.items():\n"
" if isinstance(v, t):\n"
" setattr(p, k, v)\n",
Py_file_input, d.ptr(), d.ptr());
if (result == nullptr)
throw error_already_set();
Py_DECREF(result);
#endif

return *this;
}

Expand Down
224 changes: 182 additions & 42 deletions include/pybind11/pytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,24 +499,131 @@ struct tuple_item {
};
NAMESPACE_END(accessor_policies)

struct dict_iterator {
/// STL iterator template used for tuple, list, sequence and dict
template <typename Policy>
class generic_iterator : public Policy {
using It = generic_iterator;

public:
explicit dict_iterator(handle dict = handle(), ssize_t pos = -1) : dict(dict), pos(pos) { }
dict_iterator& operator++() {
if (!PyDict_Next(dict.ptr(), &pos, &key.ptr(), &value.ptr()))
pos = -1;
return *this;
}
std::pair<handle, handle> operator*() const {
return std::make_pair(key, value);
}
bool operator==(const dict_iterator &it) const { return it.pos == pos; }
bool operator!=(const dict_iterator &it) const { return it.pos != pos; }
using difference_type = ssize_t;
using iterator_category = typename Policy::iterator_category;
using value_type = typename Policy::value_type;
using reference = typename Policy::reference;
using pointer = typename Policy::pointer;

generic_iterator() = default;
generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { }

reference operator*() const { return Policy::dereference(); }
reference operator[](difference_type n) const { return *(*this + n); }
pointer operator->() const { return **this; }

It &operator++() { Policy::increment(); return *this; }
It operator++(int) { auto copy = *this; Policy::increment(); return copy; }
It &operator--() { Policy::decrement(); return *this; }
It operator--(int) { auto copy = *this; Policy::decrement(); return copy; }
It &operator+=(difference_type n) { Policy::advance(n); return *this; }
It &operator-=(difference_type n) { Policy::advance(-n); return *this; }

friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; }
friend It operator+(difference_type n, const It &b) { return b + n; }
friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; }
friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); }

friend bool operator==(const It &a, const It &b) { return a.equal(b); }
friend bool operator!=(const It &a, const It &b) { return !(a == b); }
friend bool operator< (const It &a, const It &b) { return b - a > 0; }
friend bool operator> (const It &a, const It &b) { return b < a; }
friend bool operator>=(const It &a, const It &b) { return !(a < b); }
friend bool operator<=(const It &a, const It &b) { return !(a > b); }
};

NAMESPACE_BEGIN(iterator_policies)
/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers
template <typename T>
struct arrow_proxy {
T value;

arrow_proxy(T &&value) : value(std::move(value)) { }
T *operator->() const { return &value; }
};

/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS``
class sequence_fast_readonly {
protected:
using iterator_category = std::random_access_iterator_tag;
using value_type = handle;
using reference = const handle;
using pointer = arrow_proxy<const handle>;

sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { }

reference dereference() const { return *ptr; }
void increment() { ++ptr; }
void decrement() { --ptr; }
void advance(ssize_t n) { ptr += n; }
bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; }
ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; }

private:
handle dict, key, value;
ssize_t pos = 0;
PyObject **ptr;
};

/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor``
class sequence_slow_readwrite {
protected:
using iterator_category = std::random_access_iterator_tag;
using value_type = object;
using reference = sequence_accessor;
using pointer = arrow_proxy<const sequence_accessor>;

sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { }

reference dereference() const { return {obj, static_cast<size_t>(index)}; }
void increment() { ++index; }
void decrement() { --index; }
void advance(ssize_t n) { index += n; }
bool equal(const sequence_slow_readwrite &b) const { return index == b.index; }
ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; }

private:
handle obj;
ssize_t index;
};

/// Python's dictionary protocol permits this to be a forward iterator
class dict_readonly {
protected:
using iterator_category = std::forward_iterator_tag;
using value_type = std::pair<handle, handle>;
using reference = const value_type;
using pointer = arrow_proxy<const value_type>;

dict_readonly() = default;
dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); }

reference dereference() const { return {key, value}; }
void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } }
bool equal(const dict_readonly &b) const { return pos == b.pos; }

private:
handle obj;
PyObject *key, *value;
ssize_t pos = -1;
};
NAMESPACE_END(iterator_policies)

#if !defined(PYPY_VERSION)
using tuple_iterator = generic_iterator<iterator_policies::sequence_fast_readonly>;
using list_iterator = generic_iterator<iterator_policies::sequence_fast_readonly>;
#else
using tuple_iterator = generic_iterator<iterator_policies::sequence_slow_readwrite>;
using list_iterator = generic_iterator<iterator_policies::sequence_slow_readwrite>;
#endif

using sequence_iterator = generic_iterator<iterator_policies::sequence_slow_readwrite>;
using dict_iterator = generic_iterator<iterator_policies::dict_readonly>;

inline bool PyIterable_Check(PyObject *obj) {
PyObject *iter = PyObject_GetIter(obj);
if (iter) {
Expand Down Expand Up @@ -591,47 +698,72 @@ NAMESPACE_END(detail)

/// \addtogroup pytypes
/// @{

/** \rst
Wraps a Python iterator so that it can also be used as a C++ input iterator

Caveat: copying an iterator does not (and cannot) clone the internal
state of the Python iterable. This also applies to the post-increment
operator. This iterator should only be used to retrieve the current
value using ``operator*()``.
\endrst */
class iterator : public object {
public:
/** Caveat: copying an iterator does not (and cannot) clone the internal
state of the Python iterable */
using iterator_category = std::input_iterator_tag;
using difference_type = ssize_t;
using value_type = handle;
using reference = const handle;
using pointer = const handle *;

PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)

iterator& operator++() {
if (m_ptr)
advance();
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 <tt>operator*()</tt> */
iterator operator++(int) {
iterator rv(*this);
rv.value = value;
if (m_ptr)
advance();
auto rv = *this;
advance();
return rv;
}

bool operator==(const iterator &it) const { return *it == **this; }
bool operator!=(const iterator &it) const { return *it != **this; }

handle operator*() const {
if (!ready && m_ptr) {
reference operator*() const {
if (m_ptr && !value.ptr()) {
auto& self = const_cast<iterator &>(*this);
self.advance();
self.ready = true;
}
return value;
}

pointer operator->() const { operator*(); return &value; }

/** \rst
The value which marks the end of the iteration. ``it == iterator::sentinel()``
is equivalent to catching ``StopIteration`` in Python.

.. code-block:: cpp

void foo(py::iterator it) {
while (it != py::iterator::sentinel()) {
// use `*it`
++it;
}
}
\endrst */
static iterator sentinel() { return {}; }

friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); }
friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); }

private:
void advance() { value = reinterpret_steal<object>(PyIter_Next(m_ptr)); }
void advance() {
value = reinterpret_steal<object>(PyIter_Next(m_ptr));
if (PyErr_Occurred()) { throw error_already_set(); }
}

private:
object value = {};
bool ready = false;
};

class iterable : public object {
Expand Down Expand Up @@ -891,6 +1023,8 @@ class tuple : public object {
}
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
detail::tuple_accessor operator[](size_t index) const { return {*this, index}; }
detail::tuple_iterator begin() const { return {*this, 0}; }
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
};

class dict : public object {
Expand All @@ -906,8 +1040,8 @@ class dict : public object {
explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }

size_t size() const { return (size_t) PyDict_Size(m_ptr); }
detail::dict_iterator begin() const { return (++detail::dict_iterator(*this, 0)); }
detail::dict_iterator end() const { return detail::dict_iterator(); }
detail::dict_iterator begin() const { return {*this, 0}; }
detail::dict_iterator end() const { return {}; }
void clear() const { PyDict_Clear(ptr()); }
bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; }
bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; }
Expand All @@ -923,9 +1057,11 @@ class dict : public object {

class sequence : public object {
public:
PYBIND11_OBJECT(sequence, object, PySequence_Check)
PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check)
size_t size() const { return (size_t) PySequence_Size(m_ptr); }
detail::sequence_accessor operator[](size_t index) const { return {*this, index}; }
detail::sequence_iterator begin() const { return {*this, 0}; }
detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; }
};

class list : public object {
Expand All @@ -936,6 +1072,8 @@ class list : public object {
}
size_t size() const { return (size_t) PyList_Size(m_ptr); }
detail::list_accessor operator[](size_t index) const { return {*this, index}; }
detail::list_iterator begin() const { return {*this, 0}; }
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
template <typename T> void append(T &&val) const {
PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
}
Expand Down Expand Up @@ -1032,15 +1170,17 @@ inline str repr(handle h) {
#endif
return reinterpret_steal<str>(str_value);
}

inline iterator iter(handle obj) {
PyObject *result = PyObject_GetIter(obj.ptr());
if (!result) { throw error_already_set(); }
return reinterpret_steal<iterator>(result);
}
/// @} python_builtins

NAMESPACE_BEGIN(detail)
template <typename D> iterator object_api<D>::begin() const {
return reinterpret_steal<iterator>(PyObject_GetIter(derived().ptr()));
}
template <typename D> iterator object_api<D>::end() const {
return {};
}
template <typename D> iterator object_api<D>::begin() const { return iter(derived()); }
template <typename D> iterator object_api<D>::end() const { return iterator::sentinel(); }
template <typename D> item_accessor object_api<D>::operator[](handle key) const {
return {derived(), reinterpret_borrow<object>(key)};
}
Expand Down
Loading