如何使用 boost::p ython::迭代器与return_internal_reference
How to use boost::python::iterator with return_internal_reference?
>我有一个无法复制的类Type
,它不包含默认构造函数。我有二等A
,充当上述类的集合。第二个类通过迭代器提供访问权限,我的迭代器具有取消引用运算符:
class A {
class iterator {
[...]
public:
Type & operator*()
{
return instance;
}
private:
Type instance;
}
[...]
};
现在要公开我编写了一个看起来像这样的boost::python
代码:
class_<A>("A", [...])
.def("__iter__", iterator<A, return_internal_reference<> >())
.def("__len__", container_length_no_diff<A, A::iterator>)
;
在将打印消息添加到代码 Python 的所有迭代器操作(构造、赋值、取消引用、销毁)后,如下所示:
for o in AInstance:
print o.key
我得到输出(修剪到重要部分):
construct 0xffffffff7fffd3e8
dereference: 0xffffffff7fffd3e8
destroy 0xffffffff7fffd3e8
get key 0xffffffff7fffd3e8
在上面的代码中,这些地址只是instance
成员的地址(或方法调用中的this
)。前三行由iterator
生产,第四行由Type
用吸气剂法打印。因此,不知何故boost::python
以这样的方式包装所有内容:
- 创建迭代器
- 取消引用迭代器并存储引用
- 销毁迭代器(及其包含的对象)
- 使用在步骤二中获得的参考
很明显,return_internal_reference
的行为并不像所述那样(请注意,它实际上只是 typedef over with_custodian_and_ward_postcall<>
),只要引用方法调用的结果,它就应该保留对象。
所以我的问题是我如何使用boost::python
向 Python 公开这样的迭代器?
编辑:
正如有人指出的那样,可能不清楚:原始容器不包含类型 Type
.它包含一些BaseType
对象,我可以从中构造/修改Type
对象。所以iterator
在上面的例子中就像transform_iterator
.
如果A
是拥有Type
实例的容器,那么请考虑让A::iterator
包含Type
的句柄,而不是Type
:
class iterator {
[...]
private:
Type* instance; // has a handle to a Type instance.
};
而不是:
class iterator {
[...]
private:
Type instance; // has a Type instance.
};
在 python 中,迭代器将包含对它迭代的容器的引用。 这将延长可迭代对象的生存期,并防止在迭代期间对可迭代对象进行垃圾回收。
>>> from sys import getrefcount
>>> x = [1,2,3]
>>> getrefcount(x)
2 # One for 'x' and one for the argument within the getrefcount function.
>>> iter = x.__iter__()
>>> getrefcount(x)
3 # One more, as iter contains a reference to 'x'.
boost::python
支持此行为。 这是一个示例程序,Foo
是无法复制的简单类型; FooContainer
是一个可迭代的容器;并且FooContainer::iterator
成为迭代器:
#include <boost/python.hpp>
#include <iterator>
// Simple example type.
class Foo
{
public:
Foo() { std::cout << "Foo constructed: " << this << std::endl; }
~Foo() { std::cout << "Foo destroyed: " << this << std::endl; }
void set_x( int x ) { x_ = x; }
int get_x() { return x_; }
private:
Foo( const Foo& ); // Prevent copy.
Foo& operator=( const Foo& ); // Prevent assignment.
private:
int x_;
};
// Container for Foo objects.
class FooContainer
{
private:
enum { ARRAY_SIZE = 3 };
public:
// Default constructor.
FooContainer()
{
std::cout << "FooContainer constructed: " << this << std::endl;
for ( int i = 0; i < ARRAY_SIZE; ++i )
{
foos_[ i ].set_x( ( i + 1 ) * 10 );
}
}
~FooContainer()
{
std::cout << "FooContainer destroyed: " << this << std::endl;
}
// Iterator for Foo types.
class iterator
: public std::iterator< std::forward_iterator_tag, Foo >
{
public:
// Constructors.
iterator() : foo_( 0 ) {} // Default (empty).
iterator( const iterator& rhs ) : foo_( rhs.foo_ ) {} // Copy.
explicit iterator(Foo* foo) : foo_( foo ) {} // With position.
// Dereference.
Foo& operator*() { return *foo_; }
// Pre-increment
iterator& operator++() { ++foo_; return *this; }
// Post-increment.
iterator operator++( int )
{
iterator tmp( foo_ );
operator++();
return tmp;
}
// Comparison.
bool operator==( const iterator& rhs ) { return foo_ == rhs.foo_; }
bool operator!=( const iterator& rhs )
{
return !this->operator==( rhs );
}
private:
Foo* foo_; // Contain a handle to foo; FooContainer owns Foo.
};
// begin() and end() are requirements for the boost::python's
// iterator< container > spec.
iterator begin() { return iterator( foos_ ); }
iterator end() { return iterator( foos_ + ARRAY_SIZE ); }
private:
FooContainer( const FooContainer& ); // Prevent copy.
FooContainer& operator=( const FooContainer& ); // Prevent assignment.
private:
Foo foos_[ ARRAY_SIZE ];
};
BOOST_PYTHON_MODULE(iterator_example)
{
using namespace boost::python;
class_< Foo, boost::noncopyable >( "Foo" )
.def( "get_x", &Foo::get_x )
;
class_< FooContainer, boost::noncopyable >( "FooContainer" )
.def("__iter__", iterator< FooContainer, return_internal_reference<> >())
;
}
下面是示例输出:
>>> from iterator_example import FooContainer
>>> fc = FooContainer()
Foo constructed: 0x8a78f88
Foo constructed: 0x8a78f8c
Foo constructed: 0x8a78f90
FooContainer constructed: 0x8a78f88
>>> for foo in fc:
... print foo.get_x()
...
10
20
30
>>> fc = foo = None
FooContainer destroyed: 0x8a78f88
Foo destroyed: 0x8a78f90
Foo destroyed: 0x8a78f8c
Foo destroyed: 0x8a78f88
>>>
>>> fc = FooContainer()
Foo constructed: 0x8a7ab48
Foo constructed: 0x8a7ab4c
Foo constructed: 0x8a7ab50
FooContainer constructed: 0x8a7ab48
>>> iter = fc.__iter__()
>>> fc = None
>>> iter.next().get_x()
10
>>> iter.next().get_x()
20
>>> iter = None
FooContainer destroyed: 0x8a7ab48
Foo destroyed: 0x8a7ab50
Foo destroyed: 0x8a7ab4c
Foo destroyed: 0x8a7ab48
整个问题是我没有完全理解iterator
类应该提供什么语义。似乎只要容器存在,迭代器返回的值就必须有效,而不是迭代器。
这意味着boost::python
行为正确,有两种解决方案:
- 使用
boost::shared_ptr
- 按值返回
比我尝试的方法效率低一些,但看起来没有其他方法。
编辑:我已经制定了一个解决方案(不仅可能,而且似乎运行良好):提升 python 容器、迭代器和项目生命周期
以下是相关示例:https://wiki.python.org/moin/boost.python/iterator。
您可以通过常量/非常量引用返回迭代器值:
...
.def("__iter__"
, range<return_value_policy<copy_non_const_reference> >(
&my_sequence<heavy>::begin
, &my_sequence<heavy>::end))
这个想法是,正如你提到的,你应该绑定到容器生存期,而不是返回值的迭代器生存期。
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- C++函数中的精确"return"矩
- '[](std::list& list)<int>{return std::move(list)}(list)' 是否保证将 'list' 留空?
- 方法错误"not all control paths return a value"和方法不返回值
- GLFW glfwCreateWindowSurface return -7
- "Missing type specifier - int assumed"无法通过向主函数添加"return 0"来解决
- 如何解决类成员函数中的"return a value"错误?
- C++概念assignable_from不接受 const&-return 运算符=
- Malloc void return char 数组有时不起作用(Terry Davis 对 C++);
- 返回引用实例和非引用实例(return mystr & vs mystr)之间的区别是什么?
- 错误:调用 .. at return 语句时没有匹配函数
- 当你只使用 return 时,函数返回什么类型;在 c++ 中
- GLSL C++ glVertexAttribPointer & glDrawElements return GL_INVALID_OPERATION
- return ((fileatr & FILE_ATTRIBUTE_DIRECTORY) == 0);
- "Warning : No return statement in function returning non-void"是什么意思?
- Atom gpp编译器给出"'C:UsersadminUser' is not recognized as an internal or..."错误
- 当出现错误ld return 1退出状态时,如何编译程序
- 使用flag、return、exception、goto或break中止过程
- 我一直收到错误"cannot convert 'float*' to 'float' in return"
- 如何解决g++问题"internal compiler error: Illegal instruction min() _GLIBCXX_USE_NOEXCEPT { return __FLT_MI