intrusive smart pointers and cyclic type dependencies

Discussion in 'C++' started by Marcel Müller, Aug 8, 2009.

  1. 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.


    #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

    #define DEBUGLOG(x) printf x

    /* Interface to make a class reference countable */
    class ref_count
    { friend unsigned& get_ref(ref_count*);
    unsigned Count;
    private: // non-copyable
    ref_count(const ref_count*);
    void operator=(const ref_count&);
    ref_count() : Count(0) {}
    ~ref_count() {} // Must not call the non-virtual destructor directly.
    // 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
    T* Ptr;
    // 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)
    if (Ptr && --get_ref(Ptr) == 0)
    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;
    Marcel Müller, Aug 8, 2009
    1. Advertisements

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Leslaw Bieniasz

    templates and cyclic dependencies

    Leslaw Bieniasz, Sep 20, 2004, in forum: C++
    Victor Bazarov
    Sep 22, 2004
  2. MotoK
    Keith Thompson
    Sep 15, 2006
  3. Steven T. Hatton
    Steven T. Hatton
    Nov 30, 2006
  4. Craig Sanders

    Cyclic dependencies and namespaces.

    Craig Sanders, Sep 5, 2007, in forum: C++
    Glen Dayton
    Sep 5, 2007
  5. Marcel Müller

    Smart pointer and cyclic dependencies

    Marcel Müller, Mar 21, 2008, in forum: C++
    Marcel Müller
    Mar 22, 2008

Share This Page