用写迭代器支持在 C++ 中包装遗留 C API
Wrapping a legacy C API in C++ with write iterator support
我有一个遗留的 C API 到类似容器的对象(具体来说,Python 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,它允许您访问元组中元素的 read/write 迭代器。
前向只读迭代器定义起来并不难。这是我拥有的:
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*()
来完成(以便它给出一个左值),但我不能这样做,因为元组 "write" 需要经过 PyTuple_SET_ITEM
。我听说您可以使用 operator=
来解决这种情况,但我不确定是否应该使用通用引用 (Why no emplacement iterators in C++11 or C++14?) or a proxy class (What is Proxy Class in C++),并且我不确定代码应该是什么样子。
您要找的基本上是代理参考。您不想取消引用到 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;
};
我有一个遗留的 C API 到类似容器的对象(具体来说,Python 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,它允许您访问元组中元素的 read/write 迭代器。
前向只读迭代器定义起来并不难。这是我拥有的:
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*()
来完成(以便它给出一个左值),但我不能这样做,因为元组 "write" 需要经过 PyTuple_SET_ITEM
。我听说您可以使用 operator=
来解决这种情况,但我不确定是否应该使用通用引用 (Why no emplacement iterators in C++11 or C++14?) or a proxy class (What is Proxy Class in C++),并且我不确定代码应该是什么样子。
您要找的基本上是代理参考。您不想取消引用到 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;
};