2017-06-15 16 views
1

我有一個遺留的C API給一個類似容器的對象(特別是元組的Python C API),我想將它包裝在一個漂亮的C++ 14 API ,以便我可以使用迭代器。我應該如何去實施這個?在C++中包裝遺留的C 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*()(以便它給出一個左值)來完成,但我不能這樣做,因爲元組「write」需要經過PyTuple_SET_ITEM。我聽說您可以使用operator=來解決這種情況,但我不確定我是否應該使用通用引用(Why no emplacement iterators in C++11 or C++14?)或代理類(What is Proxy Class in C++),並且我不確定代碼的準確外觀。

+0

例:https://stackoverflow.com/a/25938871/315052 – jxh

+0

您應該考慮使用std ::迭代器爲基礎的迭代器。 –

+0

std :: iterator已被棄用,因爲C++ 17 https://stackoverflow.com/questions/37031805/preparation-for-stditerator-being-deprecated所以我認爲我們不應該再使用它了! –

回答

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; 
}; 
+0

謝謝,這很好地解釋了事情!我還有一些額外的問題:(1)在關於安裝迭代器的SO鏈接中,提到了C++標準要求某些迭代器不使用代理。爲什麼要徵收這個限制? (2)我注意到了OutputIterator,成語是將'*'運算符定義爲'return * this'。 IIUC,這不適用於讀取? –

+0

換句話說,代理對象看起來和迭代器對象(field-wise)相同,但我想在兩種情況下都不可能使用相同的東西? –

+0

@ EdwardZ.Yang我不明白這個問題,或者他們與這個問題有什麼關係。最好使用不同的類型來解決不同的問題。 – Barry