Boost logo

Boost :

From: Gavin Lambert (boost_at_[hidden])
Date: 2019-07-17 23:23:49


On 17/07/2019 23:03, JH wrote:
>> Of course, then you can't use destruction of that object to trigger the
>> close in the first place, which can have consequences for the rest of
>> your app. There are some ways around this, such as separating the
>> "real" container of the socket/buffer from the "logical" container from
>> the perspective of the rest of the app.
>
> Did you mean if to use class destructor to trigger the close, it would
> cause errors like pure virtual method called terminate called without
> an active exception? Did you allude every close need be explicitly
> called in functions not the destructors, especially the derived the
> class?

Calling close() in the destructor of the class that the handlers are
defined on is rarely the correct choice.

If you're not using shared_ptr, then you've called close() too late --
there is still an outstanding async operation but you're in the process
of deleting the object that will be called when that (later) completes,
meaning that you have a dangling pointer and will be accessing deleted
memory later.

If you are using shared_ptr, then the close() in the destructor is
(mostly) pointless, because the destructor won't get called until all
the pending operations are already ended, which *usually* means that
something else has already called close() first. (Although you have to
be a little careful of loopholes, such as when your async_read handler
doesn't start a new read, either due to error or exception. Still, as
long as you're holding the socket by value and you don't have any
pending async operations on it, it's safe to just let the socket be
destroyed without explicitly close()ing it.)

So yes, you should be calling close() from some other method. This
might be an explicit disconnection operation, or following a failed read
operation, or in the destructor of some other class that is different
from the shared_ptr used for the operations themselves.

A useful trick is to implement the pimpl idiom using a shared_ptr --
your 'outer' public class can call close() in its destructor because the
shared_ptr is to the 'inner' private class instead. Just avoid calling
"out" from the inner class to the outer class in handlers, because the
inner class will have a longer lifetime than the outer class.

> So all the callbacks need be wrapped by shared_ptr? Does that mean the
> bind callback:
>
> boost::bind(&SocketHandler::Handshake, this, boost::asio::placeholders::error)
>
> Should be replaced by
> boost::bind(&shared_ptr<SocketHandler::Handshake>, this,
> boost::asio::placeholders::error)

No, not even slightly. Have a look at the example code included with Asio.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk