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).