[C++-sig] Shared_ptr created by python wrapper doesn't know about the python reference?

Thomas Schipolowski schipo at dynamik.fb10.TU-Berlin.DE
Mon May 2 14:20:40 CEST 2005


Hello,

in my c++ extension I have two classes which refer to each other, 
creating a reference cycle. Since 'Slave' objects are useless without an 
associated 'Master', I want to store the backward reference from the 
Slave to its Master using boost::weak_ptr. To further complicate things, 
I have different variants of the Master class implemented by 
inheritance. Only references to the base class are passed around with 
boost::shared_ptr. Please have a look at the test case below. The 
scenario works as expected when the objects are created in c++:

 >>> import test as t
 >>> m = t.create_master()
Slave::Slave(master_ptr)
  master_ptr use count 2
  master_ptr addr 12411040
  weak_ptr use count 2
 >>> m.status()
MDerived::status() ==> Master::status()
  master addr 12411040
  slave use count 1
Slave::status()
  master use count 1
 >>> s = m.slave
 >>> del m
 >>> s.status()
Slave::status()
  master use count 0
 >>>

The Master reference held by the Slave is valid as long as the Python 
Master object is alive. Fine. However, this is not the case when the 
MDerived and Slave objects are constructed from Python:

 >>> m = t.MDerived()
 >>> m.slave = t.Slave(m)
Slave::Slave(master_ptr)
  master_ptr use count 3
  master_ptr addr 12410784
  weak_ptr use count 3
 >>> m.status()
MDerived::status() ==> Master::status()
  master addr 12410784
  slave use count 1
Slave::status()
  master use count 0
 >>>

The Master reference held by the weak_ptr expires as soon as the Slave 
constructor finishes, although m is still alive. I wonder what the 
temporary shared_ptr did to the master object when the ref count went to 
zero in this moment? Note however that the behavior is as expected if 
the base class m = t.Master() is used instead of m = t.MDerived().

Please advise me on how to handle this situation correctly. It should 
work the same no matter whether objects are created from python or c++.

Regards,
Thomas.

The test code (sorry, its rather long):

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/python.hpp>
using namespace boost;
using namespace boost::python;

class Master;
typedef shared_ptr<Master> pMaster;
typedef weak_ptr<Master> wpMaster;

class Slave {
public:
    Slave(pMaster m): master(m) {
        std::cout << "Slave::Slave(master_ptr)" << std::endl;
        std::cout << "  master_ptr use count " << m.use_count() << 
std::endl;
        std::cout << "  master_ptr addr " << (long)m.get() << std::endl;
        std::cout << "  weak_ptr use count " << master.use_count() << 
std::endl;
    };
    void status() {
        std::cout << "Slave::status()" << std::endl;
        std::cout << "  master use count " << master.use_count() << 
std::endl;   
    };
    wpMaster master;
};

typedef shared_ptr<Slave> pSlave;



class Master {
public:
    virtual ~Master() {};
    virtual void status() {
        std::cout << "Master::status()" << std::endl;
        std::cout << "  master addr " << (long)this << std::endl;
        std::cout << "  slave use count " << slave.use_count() << std::endl;
        if (slave) slave->status();
    };
    pSlave slave;
};


class MDerived: public Master {
public:
    //no modifications, the point is to demonstrate
    //that inheritance changes the behavior
    virtual ~MDerived() {};
    virtual void status() {
        std::cout << "MDerived::status() ==> ";
        Master::status();
    };
};

typedef shared_ptr<MDerived> pMDerived;


pMaster create_master() {
    pMaster m(new MDerived());
    pSlave s(new Slave(m));
    m->slave = s;
    return m;
}


BOOST_PYTHON_MODULE(test)
{
    class_<Slave, pSlave>("Slave", init<pMaster>())
        .def("status", &Slave::status)
        ;
    class_<Master, pMaster>("Master", init<>())
        .def_readwrite("slave", &Master::slave)
        .def("status", &Master::status)
        ;
    class_<MDerived, pMDerived, bases<Master> >("MDerived", init<>())
        ;
    def("create_master", create_master);
}

I am using VC6 with boost-build v1 and boost_1_32_0 on Windows. The 
Python version is 2.3.4






More information about the Cplusplus-sig mailing list