Deletable Pointer
Home -->
Programming Projects -->
FPS Game Code -->
Memory Code -->
Deletable Pointer
The Goal
We will create a smart pointer sort of like a std::shared_ptr pointer,
only very different because it can be deleted at any time.
Unfortunately as a consequence,
it can only be used in one thread.
It also seems like a pain to convert
a dp<T>
pointer
to a dp<const T>
pointer.
The Overall Design
Our pointer class is called dp
.
Every dp
pointer has a
std::shared_ptr
pointer to a DPNode
object.
That node object has a raw pointer to
the actual object we want to point to.
It is that raw pointer which we delete.
The DPNode Class
template
class DPNode {
public:
T * m_raw_ptr = 0;
string m_name;
DPNode() {
//Nothing to do.
}
~DPNode() {
if( this->m_raw_ptr ) {
cout << "*** Error: Shared Safe Pointer is non-null in last destructor.\n";
cout << " This would have been a \"leak\", although we are now automatically deleting the object.\n";
cout << " name = \"" << m_name << "\"\n";
}
DeleteMaybe();
}
void Delete() {
if( !this->m_raw_ptr ) {
cout << "*** Error: Shared Safe Pointer is null in delete function.\n";
cout << " name = \"" << m_name << "\"\n";
PoliteExit();
}
delete this->m_raw_ptr;
this->m_raw_ptr = nullptr;
}
void DeleteMaybe() {
if( this->m_raw_ptr ) {
Delete();
}
}
};
Note that the node has a string "name", which is used for error messages.
The DP (Deletable Pointer) Class
The following is a function defined in Fractal Block World
which exits the program if we are not "in the main thread."
If the program exits, then it prints the given error code.
void DP_ASSERT_MAIN_THREAD(int error_code);
Here is the dp class:
template <class T>
class dp {
private:
//The pointer to the node object.
std::shared_ptr< DPNode<T> > m_node_ptr;
void DerefError1() const {
cout
<< "*** Error: Shared Safe Pointer Error:\n"
<< " Attempt to use -> on null dp pointer\n";
cout
<< " Pointer name: " << GetName() << "\n";
PoliteExit();
}
void DerefError2() const {
cout
<< "*** Error: Shared Safe Pointer Error:\n"
<< " Attempt to dereference null dp pointer\n";
cout
<< " Pointer name: " << GetName() << "\n";
PoliteExit();
}
// template <class A>
// friend dp<const A> dpToConst2(dp<A> & a);
public:
dp() {
//Nothing to do.
}
~dp() {
//Nothing to do.
}
//Copy constructor.
dp(const dp<T> & b) {
if( b.m_node_ptr ) {
//If b is not-trivial,
//then must be in main thread.
DP_ASSERT_MAIN_THREAD(1000);
}
*this = b;
}
operator bool() const {
//It is ok if not in main thread.
if( !m_node_ptr ) return false;
if( !m_node_ptr->m_raw_ptr ) return false;
return true;
}
//"Assert null".
//Will exit iff the raw ptr is NOT null.
void AN() const {
if( !m_node_ptr ||
!m_node_ptr->m_raw_ptr )
{
return;
}
cout << "*** Error: \"assert null\" "
<< "failed in shared safe pointer.\n"
<< "Pointer name = \"" << GetName() << "\"\n";
PoliteExit();
}
//"Assert non-null".
//Will exit if the raw ptr is null.
void ANN() const {
if( m_node_ptr &&
m_node_ptr->m_raw_ptr)
{
return;
}
cout << "*** Error: \"assert non-null\" "
<< "failed in shared safe pointer.\n"
<< "Pointer name = \"" << GetName() << "\"\n";
PoliteExit();
}
void SetNull() {
// DP_ASSERT_MAIN_THREAD(1001);
m_node_ptr = nullptr;
}
// Returning raw pointer (does this work?).
//Type conversion operator.
operator T *() const {
DP_ASSERT_MAIN_THREAD(1002);
if( m_node_ptr ) {
return m_node_ptr->m_raw_ptr;
}
DerefError2();
return nullptr; //Will not get here.
}
T * Raw() const {
DP_ASSERT_MAIN_THREAD(1003);
if( m_node_ptr ) {
return m_node_ptr->m_raw_ptr;
}
return nullptr;
}
int GetRefCount() const {
DP_ASSERT_MAIN_THREAD(1004);
if( m_node_ptr ) {
return (int)m_node_ptr->use_count();
} else {
return 0;
}
}
dp<T> & operator=(const dp<T> & b) {
this->SetNull();
this->m_node_ptr = b.m_node_ptr;
return *this;
}
//Arrow operator.
T *operator->() const {
DP_ASSERT_MAIN_THREAD(1005); //Overly cautious.
if( !m_node_ptr ||
!m_node_ptr->m_raw_ptr )
{
if( !m_node_ptr ) cout << "m_node_ptr is null\n";
if( m_node_ptr && !m_node_ptr->m_raw_ptr ) cout << "m_raw_ptr is null\n";
DerefError1();
}
return m_node_ptr->m_raw_ptr;
}
//Dereference operator.
T &operator*() const {
DP_ASSERT_MAIN_THREAD(1006); //Overly cautious.
if( !m_node_ptr ||
!m_node_ptr->m_raw_ptr )
{
DerefError2();
}
return *(m_node_ptr->m_raw_ptr);
}
//There is no operator T *() const
//operator (and we do not want one).
bool IsNull() {
DP_ASSERT_MAIN_THREAD(1007);
if( !m_node_ptr ||
!m_node_ptr->m_raw_ptr )
{
return true;
}
return false;
}
void SetNew(T * ptr) {
this->SetNew(ptr, "");
}
void SetNew(
T * ptr,
const string & name)
{
DP_ASSERT_MAIN_THREAD(1008);
if( m_node_ptr &&
m_node_ptr->m_raw_ptr )
{
cout << "*** Error: Shared Safe Ptr :: SetNew called incorrectly\n";
#ifdef dp_NO_ERROR_CHECKING
SetNull();
#else
PoliteExit();
#endif
}
m_node_ptr = nullptr; //Just in case.
DPNode<T> * raw_node = new DPNode<T>();
m_node_ptr = std::shared_ptr< DPNode<T> >(raw_node);
m_node_ptr->m_raw_ptr = ptr;
m_node_ptr->m_name = name;
}
//If it had a name but now it is null,
//this is the last name.
string GetName() const {
DP_ASSERT_MAIN_THREAD(1009);
if( m_node_ptr ) return m_node_ptr->m_name;
return "";
}
void SetName(const string & name) {
DP_ASSERT_MAIN_THREAD(1010);
if( m_node_ptr ) {
m_node_ptr->m_name = name;
} else {
//Uh oh!!!
}
}
void Delete() {
DP_ASSERT_MAIN_THREAD(1011);
if( !m_node_ptr ||
!m_node_ptr->m_raw_ptr )
{
cout << "*** Error: deleting a null shared safe pointer\n";
cout << " ptr_name = " << GetName() << "\n";
#ifdef dp_NO_ERROR_CHECKING
return;
#else
PoliteExit();
#endif
}
m_node_ptr->Delete();
m_node_ptr = nullptr;
}
void DeleteMaybe() {
DP_ASSERT_MAIN_THREAD(1012);
if( m_node_ptr ) m_node_ptr->DeleteMaybe();
}
};
template <class T>
dp<T> NULL_DP() {
dp<T> temp;
return temp;
}