使用write iterator支持在C ++中包装遗留C API

时间:2017-06-15 19:23:24

标签: c++ c++14 cpython

我有一个类似于容器的对象(特别是Python C API to tuples)的遗留C API,我想用一个漂亮的C ++ 14 API包装,以便我可以使用迭代器。我该如何实施呢?

这里有更多细节。我们有以下现有的C API,无法更改:

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)

我们想要创建一个类,它允许您访问元组中元素的读/写迭代器。

前向只读迭代器不是很难定义。这就是我所拥有的:

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));
  }
}

但是,我不太确定如何支持写入。我必须以某种方式让*it = pyobj_ptr工作。通常,这可以通过将类型更改为PyObject*& operator*()(以便它给出左值)来完成,但我不能这样做,因为元组"写"需要经过PyTuple_SET_ITEM。我听说您可以使用operator=来解决此案例,但我不确定是否应该使用通用引用(Why no emplacement iterators in C++11 or C++14?)或代理类(What is Proxy Class in C++),而不是确切地确定代码应该是什么样的。

1 个答案:

答案 0 :(得分:3)

您正在寻找的内容基本上是代理参考。您不想取消引用PyObject*,您想要取消引用本身可以为您提供PyObject*的内容。这类似于vector<bool>等类型的迭代器的行为方式。

基本上,您希望operator*()能够为您提供以下内容:

class PyObjectProxy {
public:
    // constructors, etc.

    // read access
    operator PyObject*() const { return PyTuple_GET_ITEM(tuple, index); }

    // write access
    void operator=(PyObject* o) {
        PyTuple_SET_ITEM(tuple, index, o); // I'm guessing here
    }
private:
    PyObject* tuple;
    Py_ssize_t index;  
};