Boost logo

Boost Users :

From: Ricardo Capurro (boost-users_at_[hidden])
Date: 2003-10-03 09:58:27


Hello,

We are really very happy and impressed with boost's smart_pointers, and
we found the section "Smart Pointer Programming Techniques"
(http://www.boost.org/libs/smart_ptr/sp_techniques.html) in boost
smart_pointer documentation very useful, but we are having this problem:

We have two classes X and Y, and each object of class X holds an object
of class Y, and we need to implement a method in class X to get a
shared_ptr<Y> so we can access Y instance inside X, and we need that X
objects (that are too referenced by shared_ptr<X>) don't be deleted
until all references to the Y object are destroyed. See the problem in
this code:

class Y {};

class X : public enable_shared_from_this<X> {

Y y;

public:

shared_ptr<Y> ptr_y(); // Oops, how do we do here???

};

 

Our first approach was "Using a shared_ptr to hold a pointer to a
statically allocated object"
(http://www.boost.org/libs/smart_ptr/sp_techniques.html#static), but
this doesn't work because X objects can be destroyed and we still have
pointers to contained Y object, so we try a custom deletor like this:

template <typename Z>

class delegator_deletor {

shared_ptr<Z> ptr; // Internal pointer to father X, wich contains Y

public:

delegator_deletor( shared_ptr<Z> const & ptrz ) : ptr( ptrz ) {}

template <typename T> void operator()( T* ) { ptr.reset(); } // Null
deletor, only resets pointer to father X object

};

 

template <typename Z>

delegator_deletor<Z> make_delegator_deletor( shared_ptr<Z> ptrz ) {
return delegator_deletor<Z>( ptrz ); }

 

class X : public enable_shared_from_this<X> {

Y y;

public:

shared_ptr<Y> ptr_y() { return shared_ptr<Y>( &y,
make_delegator_deletor( shared_from_this() ) ); }

};

 

And now the problem was resolved but with one little drawback, we are
not sharing the same counter between shared_ptr<X> and shared_ptr<Y>, so
start looking boost's code to shange it in a way that it satisfies our
needs, and we added this constructor to shared_ptr class:

template<class Y,class Z>

shared_ptr(Y* y, shared_ptr<Z> const & z): px(z.px ? y : 0), pn( y ?
z.pn : detail::shared_count() ) // never throws

{

detail::sp_enable_shared_from_this(y, y, pn);

}

then we change X::ptr_y() this way:

shared_ptr<Y> X::ptr_y() {

return shared_ptr<Y>( &y, shared_from_this() );

}

 

This new constructor instructs to create a shared_ptr<> that uses the
same counter that the second's shared_ptr<> parameter, but point's to
the first raw pointer paramenter.

With this change everithing worked fine.

 

Finally our question is:

Is there an easyer way to solve our problem?

If the answer is yes we want to know it and

if the answer is no we want to know if you can enhace shared_ptr to
solve this kind of problem.

 

Thank you very much

Ricardo


#include <iostream>
#include <string>
#include <boost/smart_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

using namespace ::std;
using namespace ::boost;

#if !defined( SOLUTION1 ) && !defined( SOLUTION2 ) && !defined( SOLUTION3 ) && !defined( SOLUTION4 )
#define SOLUTION3
#endif

#if defined( SOLUTION1 )

class null_deleter {
public:
  template <typename T>
  void operator()( T* ) {}
};

#elif defined( SOLUTION2 ) || defined( SOLUTION3 )

template <typename Z>
class delegator_deletor {
  shared_ptr<Z> ptr;
public:
  delegator_deletor( shared_ptr<Z> const & ptrz ) : ptr( ptrz ) {
    cerr << "delegator_deletor<" << typeid( Z ).name() << ">()" << endl;
  }
  template <typename T>
  void operator()( T* ) {
    ptr.reset();
    cerr << "delegator_deletor<" << typeid( Z ).name() << ">()( " << typeid( T ).name() << "* )" << endl;
  }
};

template <typename Z>
delegator_deletor<Z> make_delegator_deletor( shared_ptr<Z> const & ptrz ) {
  return delegator_deletor<Z>( ptrz );
}

#endif

class Y : public enable_shared_from_this<Y> {
  Y( Y const & );
  Y& operator=( Y const & );
public:
  Y() { cerr << "Y::Y()" << endl; }
  ~Y() { cerr << "Y::~Y()" << endl; }
};

class X : public enable_shared_from_this<X> {
  Y y;
  int i;

  X( X const & );
  X& operator=( X const & );
public:
  X() : y(), i( 7 ) { cerr << "X::X()" << endl; }
  ~X() { cerr << "X::~X()" << endl; }
  shared_ptr<Y> ptr_y() {
#if defined( SOLUTION1 )
    return shared_ptr<Y>( &y, null_deleter() );
#elif defined( SOLUTION2 )
    return shared_ptr<Y>( &y, make_delegator_deletor( shared_from_this() ) );
#elif defined( SOLUTION3 )
    shared_ptr<Y> py;
    try {
      py = y.shared_from_this();
    }
    catch ( const bad_weak_ptr& ) {
      py.reset( &y, make_delegator_deletor( shared_from_this() ) );
    }
    return py;
#elif defined( SOLUTION4 )
    return shared_ptr<Y>( &y, shared_from_this() );
#endif
  }
  shared_ptr<int> ptr_i() {
#if defined( SOLUTION1 )
    return shared_ptr<int>( &i, null_deleter() );
#elif defined( SOLUTION2 ) || defined( SOLUTION3 )
    return shared_ptr<int>( &i, make_delegator_deletor( shared_from_this() ) );
#elif defined( SOLUTION4 )
    return shared_ptr<int>( &i, shared_from_this() );
#endif
  }
};

int main( const int, const char** ) {
  try {
    cerr << "(1)" << endl;
    shared_ptr<X> px( new X() );
    cerr << "(2)" << endl;
    shared_ptr<Y> py = px->ptr_y();
    cerr << "(3)" << endl;
    shared_ptr<int> pi = px->ptr_i();
    cerr << "(4)" << endl;
    px.reset();
    cerr << "(5)" << endl;
    py.reset();
    cerr << "(6)" << endl;
    pi.reset();
    cerr << "(7)" << endl;
  }
  catch ( const exception& e ) {
    cerr << "Exception caught: " << e.what() << endl;
  }
  catch ( ... ) {
    cerr << "Exception caught ..." << endl;
  }
  return 0;
}



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net