Boost Python的vector_indexing_suite似乎打破了return_internal_reference的使用。我错过了啥吗?
Posted
技术标签:
【中文标题】Boost Python的vector_indexing_suite似乎打破了return_internal_reference的使用。我错过了啥吗?【英文标题】:Boost Python's vector_indexing_suite seems to break the use of return_internal_reference. Am I missing something?Boost Python的vector_indexing_suite似乎打破了return_internal_reference的使用。我错过了什么吗? 【发布时间】:2020-03-30 22:45:52 【问题描述】:我可以创建一个类 Foo,它返回一个对类 Bar 的内部引用,并且一切似乎都工作得很好。但是,当我尝试使用 vector_indexing_suite 公开一个 Foo 的向量时,我得到了一些奇怪的行为。也就是说,当新的 Foo 附加到向量时,对 Foos 向量中 Foo 的底层 Bar 的引用会被破坏。
由于大部分代码直接来自 Boost Python 文档,我认为它应该可以工作。换句话说,我不认为我在做任何令人发指的事情。但是,我对 Boost Python 也比较陌生。我在这里错过了什么吗?
通过修改 Boost 文档中的 example 可以很容易地复制该问题,从而生成以下代码。
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/return_internal_reference.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <vector>
class Bar
public:
Bar(int x) : x(x)
int get_x() const return x;
void set_x(int x) this->x = x;
bool operator==(const Bar &other) const return other.x == x;
bool operator!=(const Bar &other) const return !(other == (*this));
private:
int x;
;
class Foo
public:
Foo(int x) : b(x)
// Returns an internal reference
Bar const& get_bar() const return b;
bool operator==(const Foo &other) const return other.b == b;
bool operator!=(const Foo &other) const return !(other == (*this));
private:
Bar b;
;
using namespace boost::python;
BOOST_PYTHON_MODULE(boosttest)
class_<Bar>("Bar", init<int>())
.def("get_x", &Bar::get_x)
.def("set_x", &Bar::set_x)
;
class_<Foo>("Foo", init<int>())
.def("get_bar", &Foo::get_bar
, return_internal_reference<>())
;
class_<std::vector<Foo>>("FooList")
.def(vector_indexing_suite<std::vector<Foo>>())
;
在 Python 中使用生成的模块时,当新的 Foo 附加到 FooList 时,对 FooList 中 Foo 的 Bar 的引用最终指向垃圾,如下所示。
>>> import boosttest
>>> foolist = boosttest.FooList()
>>> foolist.append(boosttest.Foo(2))
>>> foo_ref = foolist[0]
>>> bar_ref = foo_ref.get_bar()
>>> bar_ref.get_x()
2
>>> foolist.append(boosttest.Foo(3))
>>> bar_ref.get_x()
-572662307
>>> foo_ref.get_bar().get_x()
2
注意:为了弄清楚这里发生了什么,我设置了一个断点以在 x 的基础值发生变化时触发。从生成的堆栈跟踪来看,当 std::vector 的底层数组被换出以适应附加的新值时,该值似乎正在发生变化。测试代码证实,果然,如果多个值被附加然后删除,那么在附加到 Foo 列表时不会发生内存损坏。换句话说,由于基础数组已经调整大小,可以附加几个值而不会导致问题。
【问题讨论】:
【参考方案1】:在我尝试做的事情中,我专注于我想要在 Python 接口中实现的行为,但未能理解底层 C++ 代码中正在发生的事情。
本质上,我在 Boost Python 接口中创建的情况是指向向量元素的数据成员的指针。当向向量添加新值时,必须重新分配底层数组,从而导致对数据的任何指针或引用无效。我敢肯定,here 和许多其他 C++ 参考资料都解释了这种失效。
请注意,在 Python 方面,对 vector_indexing_suite 对象的元素 (Foo) 的任何引用都按预期运行。只有使用 Boost 的 return_internal_reference 获得的对元素成员 (Bar) 的引用才会遇到问题。作为 Boost 项目的维护者之一的stated,在使用 Boost 的 return_internal_reference 功能时要非常小心。
【讨论】:
以上是关于Boost Python的vector_indexing_suite似乎打破了return_internal_reference的使用。我错过了啥吗?的主要内容,如果未能解决你的问题,请参考以下文章
Boost.Python 创建的 dll 无法导入(遵循 Boost Python 的 QuickStart)
根据 boost python 的分步指导引发的 boost python 错误