Domanda

If I have

//test.hpp
class iA
{
public:
  virtual ~iA(){}
  virtual void foo() const = 0;
};


class A1 : public iA
{
public:
  virtual ~A1(){}
  A1(){}
  virtual void foo() const;
};

class A2 : public iA
{
public:
  virtual ~A2(){}
  A2(){}
  virtual void foo() const;
};

class B
{
public:
  B(int a);
  B(const std::string& s);

  ~B() {if (a_) {delete a_; a_ = 0;}}

  const iA& a() const
  {
    return *a_;
  }

  void foo() const;


private:
  iA* a_;
};

And I wrote the python wrapper below:

struct iAWrap : iA, boost::python::wrapper<iA>
{
  void foo() const
  {
    this->get_override("foo");
  }
};

BOOST_PYTHON_MODULE(libtest)
{
    using namespace boost::python;
    def("greet", greet);

    class_<iAWrap, boost::noncopyable>("iA")
      .def("foo",pure_virtual(&iA::foo))
        ;

    class_<B>("B",init<int>())
        .def(init<std::string>())
        .def("foo", &B::foo)
        .def("a", &B::a, return_internal_reference<1>())//line I have a question about
        ;
}

The return_internal_reference<1> binds the lifetime of the A reference returned to the invisible "self" argument of B?

È stato utile?

Soluzione

It has always helped me to think of it as though the object being returned (A) extends the lifetime of its owning object (B) to be at least as long as the returned object (A).

The return_internal_reference documentation describes the owner_arg:

The index of the parameter which contains the object to which the reference or pointer is being returned. If used to wrap a member function, parameter 1 is the target object (*this).

In the original code, the owner_arg is explicitly set to 1 indicating that the this object (B) in the member function (&B::a) invocation is the object that contains the returned object (iA).

The lifetime behavior effects is documented in with_custodian_and_ward_postcall, which states:

The ward object will not be destroyed until after the custodian [...]

The return_internal_reference documentation's class synopsis simplifies the actual inheritance chain:

template <std::size_t owner_arg = 1, class BasePolicy_ = default_call_policies>
struct return_internal_reference
    : with_custodian_and_ward_postcall<0, owner_arg, BasePolicy_>
{
  // ...
};

The return_internal_reference struct:

  • explicitly provides 0 as the custodian, setting the return object (iA) of postcall() as the custodian in the relationship.
  • passes the owner_arg (return_internal_reference defaults to 1) as the ward, setting the *this object (B) as the ward in the relationship.

Hence, the B ward object will not be destroyed until after the iA custodian object.


Here is a complete simple example demonstrating this behavior:

#include <iostream>
#include <boost/python.hpp>

class Foo
{
public:
  Foo()  { std::cout << "Foo()" << std::endl; }
  ~Foo() { std::cout << "~Foo()" << std::endl; }
};

class Bar
{
public:
  Bar()  { std::cout << "Bar()" << std::endl; }
  ~Bar() { std::cout << "~Bar()" << std::endl; }
  Foo& foo() { return foo_; }
private:
  Foo foo_;
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<Foo>("Foo");
  python::class_<Bar>("Bar")
    .def("foo", &Bar::foo, python::return_internal_reference<>())
    ;
}

Interactive Python:

>>> import example
>>> bar = example.Bar()
Foo()
Bar()
>>> foo = bar.foo()
>>> del bar
>>> del foo
~Bar()
~Foo()

Note that the lifetime of the bar ward object was extended to be at least as long as the foo custodian object.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top