Dear list,

how is it possible to have a class in C++ that can be extended from
python and that stores a weak_ptr to itself?

The reason why I want to do this:
 - using a weak_ptr to itself does not introduce a memory leak.
 - from a C++-member of that class I can call other functions that
expect a shared_ptr<>, in that case I convert the weak_ptr to a
shared_ptr and all the memory management works nicely.

The way I would suspect this to work is:
struct I {
  void setThisRCP(boost::shared_ptr<I> ptr) {
    thisRCP = ptr;
  }

  boost::shared_ptr<I> getThisRCP() const {
    return thisRCP.lock();
  }

  boost::weak_ptr<I> thisRCP;
};

Then I export this class to C++ and in python I would do:
class IDer(I):
  def __init__(self):
    I.__init__(self)
    self.setThisRCP(self)

And I would suspect that I can use that class now in C++ and in
particular calling getThisRCP() from C++ returns a valid shared
pointer. However, immediately after the call of
`self.setThisRCP(self)` the weak_ptr is already invalid and getThisRCP
returns an shared pointer to Null (None in python).

Attached to this message is a very simple example that demonstrates
how this failes for the above implementation, the ouptu for the
snipped:

print '\ntestcase for thisRCP as from python extended class:'
i = IDer()
assert i.getThisRCP() is not None

fails the assertion. However if I do the equivalent to IDer purely in
C++ (which is not really an option) then everything works (see
attachment) with the putput
testcase for thisRCP as set from C++:
42
destructor for J called ...

I suspect that the error comes from the fact that boost python create
as temporary shared_ptr that is passes to setThisRCP() which is not
the shared pointer that actually holds the instance of IDer in python
(is it actually hold as a shared_ptr?).

Do you have any proposal of how I can implement the above pattern with
boost python? Or is this completly impossible at the moment?

I also reported this issue before "storing weak_ptr to this in classes
extended from python" but at that time I wasn't using shared_ptr and
now I really suffer from the memory leak that was introduced by my
workaround which was to store the thisRCP as a "shared_ptr" and I
really need to fix that now. So here is a small little example that
does not depend on any other parts of my library.

-Holger
#include <boost/python.hpp>
#include "iostream"
#include "string"
#include "boost/weak_ptr.hpp"

using namespace boost::python;
using namespace std;
//using Teuchos::RCP;

struct I {
  virtual int foo() = 0;

  virtual ~I() {
    std::cout << "destructor for I called ..." << std::endl;
  }


  void setThisRCP(boost::shared_ptr<I> ptr) {
    thisRCP = ptr;
  }

  boost::shared_ptr<I> getThisRCP() const {
    return thisRCP.lock();
  }

  boost::weak_ptr<I> thisRCP;
};


struct IWrapper : public I, public boost::python::wrapper<I> {

  IWrapper() {

  }

  virtual int foo() {
    if( override f = this->get_override("foo") ) {
      return f();
    } else {
      return base_foo();
    }
  }


  int base_foo() {
    return 0; 
  }
};

struct J {
  virtual int foo() = 0;

  virtual ~J() {
    std::cout << "destructor for J called ..." << std::endl;
  }

  void setThisRCP(boost::shared_ptr<J> ptr) {
    thisRCP = ptr;
  }

  boost::shared_ptr<J> getThisRCP() const {
    return thisRCP.lock();
  }

  boost::weak_ptr<J> thisRCP;
};


struct JDer : public J {
  virtual int foo() {
    return 42;
  }
  
  static boost::shared_ptr<JDer> create() {
    boost::shared_ptr<JDer> ret(new JDer());
    ret->setThisRCP(ret);
    return ret;
  }

private:
  JDer() {

  }
};


BOOST_PYTHON_MODULE(thisRCPPy) 
{
  {
    typedef I ClassT;
    class_<IWrapper, boost::noncopyable >("I", init<>() )
      .def("setThisRCP", &ClassT::setThisRCP)
      .def("getThisRCP", &ClassT::getThisRCP)
      .def("foo", &ClassT::foo)
      ;
  }

  {
    typedef J ClassT;
    class_<ClassT, boost::shared_ptr<ClassT>, boost::noncopyable >("J", no_init )
      .def("setThisRCP", &ClassT::setThisRCP)
      .def("getThisRCP", &ClassT::getThisRCP)
      .def("foo", &ClassT::foo)
      ;
  }
  {
    typedef JDer ClassT;
    class_<ClassT, boost::shared_ptr<ClassT>, bases<J>, boost::noncopyable >("JDer", no_init )
      .def("__init__", make_constructor(&ClassT::create) )
      ;
  }
}

Attachment: test_rcp.py
Description: Binary data

_______________________________________________
Cplusplus-sig mailing list
Cplusplus-sig@python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig

Reply via email to