Assuming the following code:
class ChannelHandle;
class SessionHandle;
typedef ChannelHandle& ChannelHandleRef;
typedef SessionHandle& SessionHandleRef;
class RemoteChannelHandle
{
public:
RemoteChannelHandle(
ChannelHandleRef pChannelHandle, SessionHandleRef pSessionHandle);
RemoteChannelHandle(RemoteChannelHandle&&);
~RemoteChannelHandle();
void CloseChannel();
#ifndef _MSC_VER
RemoteChannelHandle& operator=(RemoteChannelHandle&&) = delete;
RemoteChannelHandle(RemoteChannelHandle const&) = delete;
RemoteChannelHandle& operator=(RemoteChannelHandle const&) = delete;
#endif
private:
LIBSSH2_CHANNEL* channel;
ChannelHandleRef channelHandle;
SessionHandleRef sessionHandle;
};
RemoteChannelHandle::RemoteChannelHandle(
ChannelHandleRef pChannelHandle, SessionHandleRef pSessionHandle)
: channel(nullptr), channelHandle(pChannelHandle), sessionHandle(pSessionHandle)
{
// OPEN SSH CHANNEL
}
RemoteChannelHandle::~RemoteChannelHandle()
{
try
{
CloseChannel();
}
catch (...)
{ }
}
void RemoteChannelHandle::CloseChannel()
{
if (channel == nullptr)
{
return;
}
// CLOSE SSH CHANNEL. THROW IF SOMETHING GOES WRONG
channel = nullptr;
}
- RemoteChannelHandle opens an SSH channel, but the cleanup requires
two steps (close + free). The first step is performed in
~RemoteChannelHandle()
, but the second will be performed in ChannelHandle's dtor; hence the data memberchannelHandle
, since we'll need to injectchannel
into it. This reference could be eliminated by performing both steps in~RemoteChannelHandle()
. sessionHandle
holds theLIBSSH2_SESSION*
necessary to open the SSH channel. Since I don't want to passLIBSSH2_SESSION*
around, this reference can't be eliminated.
The problem happens when I define a move ctor for RemoteChannelHandle. I need to clear the members of the "moved from" instance. However, there's no way to clear the reference. So, what to do here?
- Use
(const) std::shared_ptr
instead of references? I could even use naked pointers, as there's no ownership involved. I realize there's some debate regarding the use of references as data members, but apart from the move ctor, I foresee no other scenario where I would need to reseat the handles (which is why I used references in the first place). - Leave the references as they are, and create a "state" data member
specifically to check if the object is in a valid state (I can use
channel != nullptr
for this purpose)? - Any other idea?
I've searched for alternatives to this, and found no clear answer. Which could mean there's actually no clear answer, of course.
Thanks for your time.
Edit (in reply to Mankarse): ChannelHandle exists solely to perform step 2 of cleanup. Here's a simplified definition:
class ChannelHandle
{
public:
ChannelHandle();
ChannelHandle(ChannelHandle&&);
~ChannelHandle();
#ifndef _MSC_VER
ChannelHandle& operator=(ChannelHandle&&) = delete;
ChannelHandle(ChannelHandle const&) = delete;
ChannelHandle& operator=(ChannelHandle const&) = delete;
#endif
LIBSSH2_CHANNEL* GetChannel() { return channel; }
void SetChannel(LIBSSH2_CHANNEL* pChannel) { channel = pChannel; }
void FreeChannel();
private:
LIBSSH2_CHANNEL* channel;
};
SessionHandle is an RAII encapsulation for a LIBSSH2_SESSION*
. It calls libssh2_session_init()
on ctor and libssh2_session_free()
on dtor. Other similar classes take care of other steps of the SSH session's init/cleanup, but this is where we get the LIBSSH2_SESSION*
from libssh2, and SessionHandle owns it. Once again, a simplified definition:
class SessionHandle
{
public:
SessionHandle();
SessionHandle(SessionHandle&&);
~SessionHandle();
void CloseSession();
LIBSSH2_SESSION* GetSession() { return session; }
#ifndef _MSC_VER
SessionHandle& operator=(SessionHandle&&) = delete;
SessionHandle(SessionHandle const&) = delete;
SessionHandle& operator=(SessionHandle const&) = delete;
#endif
private:
LIBSSH2_SESSION *session;
};