I have a legacy C API to a container-like object (specifically, the Python C API to tuples) which I would like to wrap in a nice C++14 API, so that I can use an iterator. How should I go about implementing this?
Here's some more details. We have the following existing C API which cannot be changed:
Py_ssize_t PyTuple_GET_SIZE(PyObject *p);
PyObject* PyTuple_GET_ITEM(PyObject *p, Py_ssize_t pos);
void PyTuple_SET_ITEM(PyObject *p, Py_ssize_t pos, PyObject *o)
We want to create a class which allows you to get access to a read/write iterator on the elements in the tuple.
The forward read-only iterator is not too difficult to define. Here is what I have:
class PyTuple {
private:
PyObject* tuple;
public:
PyTuple(PyObject* tuple) : tuple(tuple) {}
class iterator {
// iterator traits
PyObject* tuple;
Py_ssize_t index;
public:
iterator(PyObject *tuple, Py_ssize_t index) : tuple(tuple), index(index) {}
iterator& operator++() { index++; return *this; }
iterator operator++(int) { auto r = *this; ++(*this); return r; }
bool operator==(iterator other) const { return tuple == other.tuple && index == other.index; }
bool operator!=(iterator other) const { return !(*this == other); }
PyObject* operator*() { return PyTuple_GET_ITEM(tuple, index); }
// iterator traits
using difference_type = Py_ssize_t;
using value_type = PyObject*;
using pointer = PyObject**;
using reference = PyObject*&;
using iterator_category = std::forward_iterator_tag;
};
iterator begin() {
return iterator(tuple, 0);
}
iterator end() {
return iterator(tuple, PyTuple_GET_SIZE(tuple));
}
}
However, I am not too sure how to support writes. I have to somehow make *it = pyobj_ptr
work. Conventionally, this would be done by changing the type to PyObject*& operator*()
(so that it gives an lvalue) but I can't do this because the tuple "write" needs to go through PyTuple_SET_ITEM
. I have heard that you can use operator=
to solve this case but I am not sure if I should use a universal reference (Why no emplacement iterators in C++11 or C++14?) or a proxy class (What is Proxy Class in C++), and am not exactly sure what the code should look like exactly.