簡體   English   中英

為什么boost :: python迭代器會跳過第一個元素?

[英]why boost::python iterator skips first element?

當我試圖在C ++中使用Python實現Python的可迭代對象時(使用boost :: python),我遇到了一個奇怪的問題。 Python似乎總是取消引用前面的一個元素,因此,在結果中它跳過第一個元素並且還取消引用“end”元素。 我也不確定我的返回值策略是否正確選擇,但如果我用std :: string替換int作為元素類型,它是唯一似乎正常工作的策略。 故意選擇了迭代器標記 - 我打算實現可迭代對象來訪問只能遍歷一次的資源。

C ++代碼:

#include <Python.h>
#include <boost/python.hpp>

#include <iostream>
#include <iterator>

int nextInstance{0};

class Foo
{
public:
    class iterator : public std::iterator<std::input_iterator_tag, int>
    {
    public:
        iterator() = delete;
        iterator& operator=(const iterator&) = delete;

        iterator(const iterator& other)
        :
            instance_(nextInstance++),
            pos_(other.pos_)
        {
            std::cout << instance_ << " copy ctor " << other.instance_ << " (" << pos_ << ")\n";
        }

        explicit iterator(int pos)
        :
            instance_(nextInstance++),
            pos_(pos)
        {
            std::cout << instance_ << " ctor (" << pos_ << ")\n";
        }

        bool operator==(iterator& other)
        {
            std::cout << instance_ << " operator== " << other.instance_ << " (" << pos_ << ", " << other.pos_ << ")\n";

            return pos_ == other.pos_;
        }

        int& operator*()
        {
            std::cout << instance_ << " operator* (" << pos_ << ")\n";

            return pos_;
        }

        iterator operator++(int)
        {
            ++pos_;

            std::cout << instance_ << " operator++ (" << pos_ << ")\n";

            return *this;
        }

        ~iterator()
        {
            std::cout << instance_ << " dtor\n";
        }

    private:
        const int instance_;
        int       pos_{0};

    };

    iterator begin()
    {
        std::cout << "begin()\n";

        return iterator(0);
    }

    iterator end()
    {
        std::cout << "end()\n";

        return iterator(3);
    }
};

BOOST_PYTHON_MODULE(pythonIterator)
{
    boost::python::class_<Foo, boost::noncopyable>("Foo", boost::python::init<>())
        .def("__iter__", boost::python::iterator<Foo, boost::python::return_value_policy<boost::python::copy_non_const_reference>>{});
}

Python代碼:

#!/usr/bin/python

import pythonIterator

foo = pythonIterator.Foo()

for i in foo:
    print i

輸出:

end()
0 ctor (3)
begin()
1 ctor (0)
2 copy ctor 1 (0)
3 copy ctor 0 (3)
1 dtor
0 dtor
4 copy ctor 2 (0)
5 copy ctor 3 (3)
3 dtor
2 dtor
4 operator== 5 (0, 3)
4 operator++ (1)
6 copy ctor 4 (1)
6 operator* (1)
6 dtor
1
4 operator== 5 (1, 3)
4 operator++ (2)
7 copy ctor 4 (2)
7 operator* (2)
7 dtor
2
4 operator== 5 (2, 3)
4 operator++ (3)
8 copy ctor 4 (3)
8 operator* (3)
8 dtor
3
4 operator== 5 (3, 3)
5 dtor
4 dtor

您的增量后運算符中存在錯誤。 具體而言,您執行的是預先 -Increment,不得發布 -Increment:

iterator operator++(int)
{
    ++pos_;
    return *this;  // return value *after* having incremented it
}

正確的實施將是:

iterator operator++(int)
{
    iterator tmp(*this);
    ++pos_;
    return tmp; // return saved tmp *before* having incremented it
}

在修復之后:

>>> list(pythonIterator.Foo())
... snip lots of output ...
[0, 1, 2]

哇哇 感謝最終向我展示了第一個自包含的Boost Python示例。

所以,讓我通過建議使用Boost Iterator為您處理迭代器復雜性來回報您的服務:

住在Coliru

#include <Python.h>
#include <boost/python.hpp>
#include <boost/iterator/iterator_facade.hpp>

class Foo
{
public:
    struct iterator : boost::iterator_facade<iterator, int, boost::single_pass_traversal_tag, int>
    {
        iterator(int i) : current_(i) {}

        bool equal(iterator const& other) const { return current_ == other.current_; }
        int dereference() const { return current_; }
        void increment() { ++current_; }
    private:
        int current_;
    };

    iterator begin() { return 0; }
    iterator end()   { return 3; }
};

BOOST_PYTHON_MODULE(pythonIterator)
{
    boost::python::class_<Foo, boost::noncopyable>("Foo", boost::python::init<>())
        .def("__iter__", boost::python::iterator<Foo, boost::python::return_value_policy<boost::python::return_by_value>>{});
}

打印:

$ ./test.py 
0
1
2

當然,使迭代器返回副本的選擇受到源范圍缺失的啟發。 (顯然iterator_facade完全適用於lvalue-refs,如果你需要的話)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM