|
Boost Users : |
Subject: Re: [Boost-users] bind with weak_ptr
From: Igor R (boost.lists_at_[hidden])
Date: 2011-10-11 06:08:51
> The link you gave.
> It contains a couple template classes, from what I understand overload
> operator ->*(), which I've never even seen before. I assume it is simply
> trying to replace the internals of the shared_ptr.
No, it doesn't replace any internals and doesn't hack anything. The
code just extends bind functionality at its extension point.
I'll explain and I'm sure you'll be convinced it's quite simple.
First of all, in order to be able to bind a smart-pointer (or a
pointer-like object), one should provide get_pointer() free function
that accepts a smart-ptr and returns a raw pointer - or, to be more
exact, returns *something* that supports "pointer-to-member" operator
->*.
Out of the box, boost provides get_pointer for shared_ptr, auto_ptr,
optional, intrusive_ptr and some other types, making them bindable.
The same way you can make other smart pointers bindable, like
ATL::CComPtr, etc.
So, just to make weak_ptr bindable, you could add the following trivial lines:
namespace boost
{
template<class T>
T *get_pointer(const weak_ptr<T> &weak)
{ return weak.lock().get(); }
}
But this wouldn't solve the whole problem, as expired weak_ptr's get()
returns empty shared_ptr, whose get() returns null pointer. Although,
null pointer would crash your code in a more consistent and nice way
:).
This means that instead of raw pointer we should return some
"adapter", which supports operator ->*, and tests whether it holds
null or not. Operator ->*, in turn, by definition must return a
"callable", i.e. an object or a function, which can be called with the
appropriate arguments.
This is exactly what "ptr_adapter" and "invoker" classes do:
ptr_adapter stores the raw ptr, and when its operator ->* is being
invoked with a specific member function (during the binder invocation)
it constructs and returns "invoker" instance, which holds both raw ptr
to the object and the pointer to the member function that should be
called. Now, the invoker's operator() just tests whether it holds null
ptr or not and performs the actual invocation of the member function.
>> What I meant there is that you can use raw pointer *tracking* it with a separate weak_ptr.
>
> It wouldn't matter if I tracked it or not.
> Sure, I can check if the pointer is good before I hand a bind to
> boost::asio::io_service. I can check it again when io_service calls me back.
> However, the crash is going to occur when io_service actually does the callback and I have no control there.
I'm afraid you misunderstand what "tracking" is. Testing smart-ptr
when you create the binder certainly doesn't help - it should be test
just before the *invocation*. So, you should pass such a tracking
object to the entity that is supposed to invoke your functor. When
this entity invokes your functor, it should first lock & test the
tracking object:
auto locked = weak.lock();
if (locked)
invokeTheFunctor();
Of course, if the caller is not your own module, but asio::io_service,
you cannot use this technique without defining additional wrapers.
> I understand the claim that it would be up to the user on how to handle the
> instance no longer existing. Maybe they want to throw an exception, maybe they
> want to do nothing, etc. However, I don't see a way to even be _informed_ of
> the situation in order to handle it at all. At least not without a workaround like what you posted. It's not in the boost library itself.
Being informed is exactly the problem here :).
The only reasonable way to inform is to throw an exception, which
means that bind invocation process will loose its "no-throw" guarantee
(which I belive, currently does exist).
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