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 错误

Boost.Python 如何拥有 C++ 类?

Boost.Python 对象的 id

Boost Python,Visual Studio链接到错误的boost dll

Boost.Python不是.lib