2015-02-11 104 views
4

当我试图在C++中使用Python实现可迭代对象时(使用boost :: python),我遇到了一个奇怪的问题。 Python似乎总是提前引用一个元素,因此,它跳过了第一个元素,并且还引用了“end”元素。我也没有信心,如果我的返回值策略选择正确,但它是唯一一个似乎正常工作,如果我用std :: string作为元素类型替换int。迭代器标签被故意选择 - 我打算实现可迭代对象来访问只能遍历一次的资源。为什么boost :: python迭代器跳过第一个元素?

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 

回答

4

您的递增运算符的错误。具体而言,您执行的是预先 -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] 
+0

谢谢。如果有人感兴趣为什么后增量(不是预增量)在这里 - 这是boost :: python迭代器包装器所需的。 – kpx1894 2015-02-11 22:34:42

3

哦,哇。感谢您终于向我展示了第一个自包含的Boost Python示例。

所以,让我建议使用加速迭代处理迭代器的复杂性对你报答你的服务:

Live On 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 

当然,使迭代器返回的选择c opies的灵感源于无源的范围。 (显然iterator_facade是完全适应左岸裁判,如果你需要他们)

+1

这里是** [Live Of The Glorious Coliru](http://coliru.stacked-crooked.com/a/29827dfd0a5731d1)** – sehe 2015-02-11 22:03:34

+0

诅咒!你征服的另一个标签。 (;感谢Boost.Python Coliru的例子!我还没有尝试过在我的Boost.Python答案中使用它。 – 2015-02-11 22:16:08

+0

当我在Boost中发现了一些新东西的时候,又一天。:-)谢谢。 – kpx1894 2015-02-11 22:39:45