Boost Python的vector_indexing_suite似乎破坏了return_internal_reference的使用。我想念什么吗?

时间:2020-03-30 22:45:52

标签: python c++ boost

我可以创建一个Foo类,该类返回对Bar类的内部引用,并且一切似乎都可以正常工作。但是,当我尝试使用vector_indexing_suite公开Foo的向量时,我得到一些奇怪的行为。也就是说,当将新的Foo附加到向量上时,对向量中Foos的基础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中使用结果模块时,对FooList中Foo的Bar的引用最终指向垃圾,这是因为将新的Foo附加到FooList上,如下所示。

>>> 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 List时不会发生内存损坏。换句话说,由于基础数组已被调整大小,因此可以附加多个值而不会引起问题。

1 个答案:

答案 0 :(得分:0)

在我尝试做的事情中,我专注于在Python界面中想要的行为,但是没有意识到底层C ++代码中正在发生的事情。

本质上,我在Boost Python接口中创建的情况是指向向量元素的数据成员的指针。当向向量添加新值时,必须重新分配基础数组,从而导致对数据的任何指针或引用均无效。 here对此无效进行了解释,我敢肯定,在许多其他C ++参考中。

请注意,从Python方面来看,对vector_indexing_suite对象的元素(Foo)的任何引用均表现出预期的行为。只是使用Boost的return_internal_reference获得的对元素成员(Bar)的引用出现了问题。作为stated的Boost项目维护者之一,在使用Boost的return_internal_reference功能时要非常小心。