intrusive smart pointers and cyclic type dependencies

M

Marcel Müller

I have two classes A and B that are managed by intrusive smart pointers.
But A as well as B can contain managed references to the other class. So
the smart pointer must deal with incomplete types.

boost::intrusive_ptr can deal with that but it is inconvenient to define
the two helper functions for each type, especially in case of forward
declarations of a class.

Currently I tried the implementation below.
(No thread safety for simplification.)

First of all I am not sure if it has some undefined behavior because of
the incomplete type.
Furthermore I tried to do some optimization to access the reference
count inline if the type is no longer incomplete. Unfortunately the
trick did not work, because get_ref(T*) is still preferred over
get_ref(ref_count*) even if T is known to derive from ref_count. Is
there a way to avoid the real function call for complete types?
If so it could also be used for the call to delete_me by adding a class
ref_count_virt with a virtual destructor.


Marcel

--------------------
#include <stdio.h>
#include <assert.h>

#if (defined(__IBMC__) && __IBMC__ <= 300) || (defined(__IBMCPP__) &&
__IBMCPP__ <= 300)
#define bool signed char
#define true 1
#define false 0
#endif

#define DEBUGLOG(x) printf x


/* Interface to make a class reference countable */
class ref_count
{ friend unsigned& get_ref(ref_count*);
private:
unsigned Count;
private: // non-copyable
ref_count(const ref_count*);
void operator=(const ref_count&);
protected:
ref_count() : Count(0) {}
~ref_count() {} // Must not call the non-virtual destructor directly.
public:
// Checks whether the object is currently unique.
bool RefCountIsUnique() const;
// Checks whether the object is not under control of a int_ptr.
// This is the case when the object is just constructed and not yet
// assigned to an int_ptr instance or if the object is about to be
// deleted.
bool RefCountIsUnmanaged() const;
};


inline unsigned& get_ref(ref_count* r)
{ return r->Count;
}

template <class T>
unsigned& get_ref(T* r);

template <class T>
void delete_me(T* ptr);


/* Intrusive reference counted smart pointer. */
template <class T>
class int_ptr
{private:
T* Ptr;
public:
// Initialize a NULL pointer.
int_ptr() : Ptr(NULL) {}
// Store a object under reference count control.
int_ptr(T* ptr) : Ptr(ptr) { if (Ptr) ++get_ref(Ptr); }
// Copy constructor
int_ptr(const int_ptr<T>& r) : Ptr(r.Ptr) { if (Ptr) ++get_ref(Ptr); }
// Destructor, frees the stored object if this is the last reference.
~int_ptr() { if (Ptr && --get_ref(Ptr) == 0)
delete_me(Ptr); }
// Basic operators
T* get() const { return Ptr; }
operator T*() const { return Ptr; }
T& operator*() const { assert(Ptr); return *Ptr; }
T* operator->() const { assert(Ptr); return Ptr; }
// assignment
void assign(T* ptr);
int_ptr<T>& operator=(T* ptr){ assign(ptr); return *this; }
int_ptr<T>& operator=(const int_ptr<T>& r)
{ assign(r.Ptr); return *this; }
void swap(int_ptr<T>& r)
{ T* tmp = Ptr; Ptr = r.Ptr; r.Ptr = tmp; }
};

template <class T>
void int_ptr<T>::assign(T* ptr)
{ if (ptr)
++get_ref(ptr);
if (Ptr && --get_ref(Ptr) == 0)
delete_me(Ptr);
Ptr = ptr;
}



struct A;

struct B;

// Base class to force non-trivial pointer conversions.
struct C
{ const unsigned magic;
C::C() : magic(0xc0c0c0c0) {}
};

struct A : public C, public ref_count
{ const unsigned magic;
int_ptr<B> b;
A() : magic(0xa0a0a0a0) { DEBUGLOG(("A(%p)::A()\n", this)); }
~A() { DEBUGLOG(("A(%p)::~A()\n", this)); }
};

struct B : public C, public ref_count
{ const unsigned magic;
int_ptr<A> a;
B() : magic(0xb0b0b0b0) { DEBUGLOG(("B(%p)::B()\n", this)); }
~B() { DEBUGLOG(("B(%p)::~B()\n", this)); }
};


int main()
{
int_ptr<A> a = new A();
int_ptr<B> b = new B();
// Make cyclic dependency
a->b = b;
b->a = a;
// Cancel cyclic dependency
a->b = NULL;
return 0;
}


// proxy for type safety
inline unsigned& get_ref_proxy(ref_count* r)
{ return get_ref(r);
}

template <class T>
unsigned& get_ref(T* r)
{ DEBUGLOG(("get_ref(T* %p)\n", r));
return get_ref_proxy(r);
}

template <class T>
void delete_me(T* ptr)
{ delete ptr;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top