为什么boost::python迭代器跳过第一个元素
why boost::python iterator skips first element?
当我试图在C++中为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_ << " dtorn";
}
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
您的后增量运算符中有一个错误。具体来说,您实现的是前增量,而不是后-增量:
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]
哦,哇。感谢您最后向我展示了第一个自包含的BoostPython示例。
因此,让我通过建议使用Boost迭代器来为您处理迭代器的复杂性来回报您的服务:
在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
完全适合左值参考)
相关文章:
- 如何实现 Front() 方法以返回模板化双向链表C++的第一个元素?
- 对的排序向量 (std::vector<pair<int, int>>) 按对的第一个元素搜索并更新第二个元素值
- C++ queue.front();为什么不从第一个元素开始呢?
- 指向数组基址的指针而不是指向第一个元素的指针有什么优点?
- 静态堆栈函数不会 1) 输入第一个元素 2)添加新元素时识别旧元素
- 从队列中删除第一个元素C++
- 如果第一个元素包含任何零,则我的程序以不希望的方式运行
- 为什么数组不打印第一个元素?
- 在数组中显示第一个元素用户输入
- 消除集合的第一个元素
- 为什么指向矢量第一个元素的指针会丢失?
- 如何打印出常量字符串的第一个元素?
- 读取数组的第一个元素还是第 4000 个元素更快?
- 如何在数组的一部分中查找特定值的第一个元素
- 对数组进行排序的算法,先是第一个元素,然后是前 2 个元素,然后是前 3 个元素,依此类推
- 访问指向对象指针向量的指针的第一个元素?
- 引用保留向量中的第一个元素
- 指向数组的指针是否应该等于指向其第一个元素的指针?
- 尝试将参数包的第一个元素作为函数调用,并将包的其余部分作为参数传递给它
- 代码是否有效.如果我想显示第一个元素?如果不是,那么 s.begin() 会返回什么?