Reputation: 8074
I need single ownership for an object because I need to be able to destroy it on demand (this makes sense sometimes; in this case the object represents a logged-in session that, for security reasons, the user wants to close). Let's call this object session
. Other client objects keep references to session
, but, of course, it may be dead when the clients access the reference.
What I'm after is a 'safe reference' that is notified when the original object is destroyed and reports this gracefully (exception, boolean) to the client, instead of a segfault.
Does anything like this exist? Preferable using what's available in standard C++/Boost. Preferably C++03. shared_ptr
with weak_ptr
is almost what I'm after, if only the shared_ptr
s didn't extend the lifetime of session
. I need to guarantee that session
has been destroyed and a stray shared_ptr
would prevent that.
Upvotes: 3
Views: 885
Reputation: 1334
Session class is wrapped inside SharedSession, which has a single reference to Session. SharedSession class provides behavior of Session class to clients. Client can close session and all other references when try to access the session get an exception. Locking in case of threads has not been covered in this code example.
#include<iostream>
#include<utility>
//- single object ownership
//- ensure shared object is not deleted by one of the reference deletion;
//- client should be notified when pointed memory is deleted explicitly on request.
//Proxy class for Session
class SharedSession{
//Session class
class Session{
public:
Session()
{
std::cout<<"Session ctr"<<std::endl;
}
bool isValidSession()
{
std::cout<<"Is valid session"<<std::endl;
}
bool login(std::string user,std::string password,std::string& sessionId)
{
std::cout<<"Login session"<<std::endl;
//authenticate user - password and generate session id.
sessionId = "abd123";
return true;
//return false //in case of failure
}
~Session()
{
std::cout<<"Session dtr"<<std::endl;
}
private:
std::string _userName;
std::string _password;
std::string _sessionId;
Session(const Session& rhs){}
Session& operator=(const Session& rhs){}
};
Session* _sessionInstance;
//number of SharedSession objects created
static int _sessionCount;
//single ownership of sesion maintained in pair
static std::pair<int,Session*> _sessionPair;
//maintain state of session
static std::pair<bool,int> _sessionValid;
public:
SharedSession()
{
++_sessionCount;
std::cout<<"Shared Session "<<_sessionCount<<std::endl;
if(_sessionCount == 1)
{
_sessionInstance = new Session();
_sessionPair = std::make_pair(1,_sessionInstance);
_sessionValid = std::make_pair(true,_sessionCount);
}
if(!_sessionValid.first)
throw -1;//session is deleted
else
{
_sessionValid.second = _sessionCount;
_sessionInstance = NULL;
}
}
~SharedSession()
{
std::cout<<"Shared session dtr "<<_sessionValid.second<<std::endl;
if(_sessionValid.second == 1 && _sessionValid.first)
{
delete (_sessionPair.second);
std::cout<<"session deleted"<<std::endl;
_sessionPair.second = NULL;
_sessionValid.first = false;
}
_sessionValid.second -= 1;
--_sessionCount;
}
bool closeSession()
{
//invalidate session
_sessionValid.first = false;
std::cout<<"close session"<<std::endl;
delete _sessionPair.second;
}
bool isValidSession()
{
std::cout<<"session state "<<_sessionValid.first<<std::endl;
if(_sessionValid.first)
_sessionPair.second->isValidSession();
else
//notify client about deleted session
throw -1;
}
bool login(std::string user,std::string password,std::string& sessionId)
{
if(_sessionValid.first)
return _sessionPair.second->login(user,password,sessionId);
//notify client about deleted session
throw -1;
}
//any other operations which Session class exposes
};
int SharedSession::_sessionCount = 0;
std::pair<int,SharedSession::Session*> SharedSession::_sessionPair;
std::pair<bool,int> SharedSession::_sessionValid;
int main()
{
SharedSession session1;
SharedSession session2;
try
{
session1.closeSession();
session2.isValidSession();
}
catch(int& error)
{
std::cout<<"Exception occured "<<error<<std::endl;
std::cout<<"Session already deleted."<<std::endl;
}
return 0;
}
Upvotes: 0
Reputation:
Maybe something like:
class Session
{
private:
class Implementation {};
public:
Session()
: m_self(0) // You might do better, here
{}
~Session() { delete m_self; }
private:
Session(const Session&); // No copy
Session& operator = (const Session&); // No copy
public:
bool valid() const { return bool(m_self); }
void terminate() { delete m_self; m_self = 0; }
private:
Implementation* m_self;
};
The class above has no similarity to std::shared_ptr or std::unique_ptr. Each session object can be passed by reference, only.
If you need to signal the termination of the session (or other events) I suggest putting boost::signal2 into the implementation.
Upvotes: 1
Reputation: 89
What about something like this :
#include <assert.h>
template <class T>
class notify_ptr
{
public:
notify_ptr(T* ptr):m_ptr(ptr){};
void kill()
{
if (m_ptr)
{
delete m_ptr;
m_ptr = 0;
}
}
bool isAlive()
{
return m_ptr!=0;
}
T* operator->()
{
if (!m_ptr)
{
assert("Using a dead reference !");
return 0; // segfault
}
return m_ptr;
}
private:
T* m_ptr;
};
class session
{
public:
session():i(0){}
void AddOne(){++i;}
private:
int i;
};
void bar(notify_ptr<session>& mySession)
{
mySession->AddOne();
mySession.kill();
}
int main()
{
notify_ptr<session> ptr(new session);
bar(ptr);
if (ptr.isAlive())
{
ptr->AddOne();
}
return 0;
}
Upvotes: -1
Reputation: 275385
There is a fundamental problem with the design you are requesting.
Suppose you have a session and a user of the session. The user checks that the session is valid, then uses it. Meanwhile the session becomes invalid, between checking and use. weak_ptr
deals with this by allowing the user to upgrade to shared_ptr
, then check and use that. Your design precludes that.
If you are willing to ignore that problem, I have a solution.
The object keeps a shared_ptr<void>
(allocated as a char
) as a member. It exposes weak_ptr<void>
which can be used to track its lifetime, but not determine it. The object itself is unique_ptr
stored, and the users keep a raw pointer and lifetime tracker pair.
This does not provide for immediate update of lifetime termination: for that use the callback pattern.
Upvotes: 2