Sign in to follow this  
ElPeque2

help boost::bind and shared pointers

Recommended Posts

I have this problem. Im using boost::bind for programming the call-back mechanism for a gui system and to pass messages (possibly, delayed messages). My problem is, that when i bind member functions, if i specify the instance of the class as a shared_ptr, then i get memory leaks because the binding's shared_ptr to the instance keeps it alive, and it potentially produces cyclic references that in the current model, would be complicated to break. And of course, if i use raw pointers to the binding instances, then i'll get a crash if the instance has been destroyed and i try to execute the binding function. So what i would need, is to use bind with weak pointers, and just do nothing if locking it fails. I'm not sure how to do that, or how to assemble a good walkaround. I'd like it to be as automatic as possible. Thanks for your time, i appreciate any help :)

Share this post


Link to post
Share on other sites
Quote:
So what i would need, is to use bind with weak pointers, and just do nothing if locking it fails.
Can't do it directly, unfortunately. This is something I've really wanted for boost::bind and boost::signal for awhile. There's half-decent reasons behind it (no always-applicable response for dead ptrs), but I think the main problem is that it doesn't fall cleanly on any one side of the bind/signal/smart_ptr trifurcation.

A workaround is to boost::bind to a static function which takes a weak_ptr, locks it, and calls the non-static version. Heck, you could wrap this up in some template functions without much trouble. There's also other event libraries out there which don't suffer from this limitation, because they implement their own weak_ptr-like things.

Share this post


Link to post
Share on other sites
I don't think i could do the template wrap up you mention without breaking things. Im quite new to programming my own templates.

Someone at the boost maillist suggested me looking at the Thread Safe version of Signals. It seems you set up the signal to track the shared pointers, and automatically disconnects the ones that expire. I think i'll take a look at it.

But im still looking for more ideas, and thanks Sneftel for your time :).

PD: I'd appreciate some guidelines in case i decide to try to make the template wrapper you proposed.

Share this post


Link to post
Share on other sites
Quote:
Original post by ElPeque2
Someone at the boost maillist suggested me looking at the Thread Safe version of Signals. It seems you set up the signal to track the shared pointers, and automatically disconnects the ones that expire. I think i'll take a look at it.

That's the "trackable" thing. It works okay... but forces you to use raw, instead of shared, pointers. BTW, it's in main boost::signals; you don't need the thread safe version.

EDIT: Now that I look at it, thread-safe signals has discarded trackable in favor of weak_ptr. Awesome. Go with that.

Share this post


Link to post
Share on other sites
What do you mean by "thread-safe signals has discarded trackable in favor of weak_ptr."

http://www.comedi.org/projects/thread_safe_signals/libs/thread_safe_signals/doc/html/signals/tutorial.html#id2566661

you mean this:


deliverNews.connect(signal_type::slot_type(&NewsMessageArea::displayNews,
newsMessageArea, _1).track(newsMessageArea));


is no longer necesary?

Share this post


Link to post
Share on other sites
Oh, that's still necessary. What isn't necessary is making all your classes inherit from trackable, duplicating the ref-count stuff that shared_ptr already offers.

Share this post


Link to post
Share on other sites
this is the wrapper code i use - i avoid an exposed global get() type function since the weakly bound object could go out of scope between the getting of the underlying pointer and the call of the bound function/method (fundamental difference with shared_ptr get() binding). here, at the time of the call a strong reference is used else an exception is thrown. its convenient since with a multicast delegate (bound multimethod? ) type thing since we can just iterate in a try block and remove 'dangling' type functions.

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>

using namespace std;
using namespace boost;

class A
{
public:
A() { }
virtual ~A() { }
void method( int value) { cout << "method() " << value << endl; }
};

template< typename T >
class from_weak_impl
{
public:
// result_type must defined and be const,
// we must return a strong ptr to prevent another thread unlocking between calls
typedef const shared_ptr< T> result_type;
weak_ptr< T> p;
inline from_weak_impl( const weak_ptr< T> &p0) : p( p0) { }
inline from_weak_impl( const shared_ptr< T> &p0) : p( p0) { }
inline result_type operator ()() const {
// throws if bad...
return shared_ptr< T>( p);
}
};

template<class T>
inline from_weak_impl< T> from_weak( const weak_ptr<T> & p)
{
return from_weak_impl< T>( p);
}

template<class T>
inline from_weak_impl< T> from_weak( const shared_ptr<T> & p)
{
return from_weak_impl< T>( p);
}

int main()
{
function< void( int) > f;

{
shared_ptr< A> a( new A ) ;
shared_ptr< A> sa = a;
weak_ptr< A> wa = a;

f = bind( &A::method, sa, _1 ); // strong
f = bind( &A::method, bind( from_weak( wa)), _1 ); // weak
f = bind( &A::method, bind( from_weak( sa)), _1 ); // weak
}

// should throw since object a is out of scope
f( 123);
}




Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
It seems what you want is enable_shared_from_this.
How so?

Share this post


Link to post
Share on other sites
i dont understand the bind used inside the bind, chairthrower :S

and i was hoping it not to throw, but do nothing. (just not do the call if the instance has been destroyed).

Share this post


Link to post
Share on other sites
It seems I have completely misunderstood the issue.
It seems the OP doesn't want the callback to own the object (seems fairly weird to have a callback that can become empty at any time though)

Share this post


Link to post
Share on other sites
Quote:
i dont understand the bind used inside the bind, chairthrower :S


The inner bind is to yield a strong (shared_ptr) reference from the weak pointer. This has to be delayed until the time of the call and hence it is another bind. i totally agree that the syntax becomes more complicatated (ugly) but with the way bind works i dont think its possible to avoid the inner bind() while having the safety that comes from the locking of the weak reference to yield a strong reference that can then be subsequently used safely.

note that
f = bind( &A::method, bind( from_weak( ap)),  _1  );


has the same effect as the below except that lock() doesnt throw unfortunately and save us from executing the outer bind function
f = bind( &A::method, bind( &weak_ptr< A>::lock, wp),  _1  );


I said before that we have to ensure that we have a strong reference for the entire duration of the call (not guaranteed by a raw pointer) to avoid the possibility of another thread releasing the resource while we are in the middle of the call. in fact the more obvious case is that the call itself could release shared references which in turn release the instance object while we are doing something with it. using the stron g ref we can delay these releases until after we have finished our work.

the exception stuff is just a cheap way of avoiding the real call if the instance has been destroyed and as means of communication to facilitate clearing the resource which is the bound function. you could use it something like this.

template< class C, class A0> 
void call_each( C &c, const A0 &a1)
{
for( typename C::iterator ip = c.begin(); ip != c.end();)
{
bool ok = true;
try { (*ip)( a1); }
catch( const bad_weak_ptr &e) { ok = false; }
if( !ok) ip = c.erase( ip);
else ++ip;
}
}

int main()
{

list< function< void( int) > > callbacks;
{
shared_ptr< A> ap( new A);
callbacks.push_back( bind( &A::method, bind( from_weak( ap)), _1 ));
}
// does nothing since ap is out of scope
call_each( callbacks, 123);
}


Perhaps it would be better to explicitly test for the failure case and avoid the overhead of the exception. (boost::lambda::if_then ?). Another possibility is that more recent releases of boost 1.35 allow you to apply a visitor pattern which could be used to explicitly verify whether the object is ok to call. this is pretty cool- the problem is that in my experimentation at least its not possible to visit a boost::function because of the complete type erasure that goes on with boost::function. also its potentially quite slow.

The other tradeoff in the above approach is that we have dangling resources that persist until we come along and make the call and then potentially go about cleaning them up if the underlying instance is not in scope.


Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this