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.


    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;
    }
     
    Marcel Müller, Aug 8, 2009
    #1
    1. Advertising

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++
    Replies:
    3
    Views:
    422
    Victor Bazarov
    Sep 22, 2004
  2. MotoK
    Replies:
    59
    Views:
    1,915
    Keith Thompson
    Sep 15, 2006
  3. Steven T. Hatton
    Replies:
    2
    Views:
    1,611
    Steven T. Hatton
    Nov 30, 2006
  4. Craig Sanders

    Cyclic dependencies and namespaces.

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

    Smart pointer and cyclic dependencies

    Marcel Müller, Mar 21, 2008, in forum: C++
    Replies:
    1
    Views:
    649
    Marcel Müller
    Mar 22, 2008
Loading...

Share This Page