M
Marcel Müller
I have a problem with a cyclic dependency of two classes:
class Iref_count // Interface for the intrusive_ptr
{ friend class int_ptr_base; // for access to Count
private:
volatile unsigned Count;
protected:
Iref_count() : Count(0) {}
// You must not call the non-virtual destructor directly.
~Iref_count() {}
};
class Slice;
class Iterator
{ intrusive_ptr<Slice> root;
// some more stuff
};
class Slice : public Iref_count
{ scopec_ptr<Iterator> start;
scopec_ptr<Iterator> stop;
// some more stuff
};
To get this to compile either scoped_ptr or intrusive_ptr have to accept
an incomplete type.
Unfortunately I did not have luck with this so far. Neither Slice nor
Iterator are PODs. And the forward declaration of Slice generates wrong
code in intrusive_ptr at the conversion from Iref_count* to Slice*. The
pointer value is not translated by the offset of Iref_count.
Note that intrusive_ptr is not boost::intrusive_ptr, because that won't
compile on my platform. I wrote something similar (see below). However,
I am unsure whether boost::intrusive_ptr would have defined behavior in
this case, and if it does so, why?
Any Ideas?
The cyclic dependency is really needed from the designs point of view.
/* Abstract non-template base class of int_ptr */
class int_ptr_base
{protected:
Iref_Count* Ptr;
public:
// Store a new object under reference count control
// or initialize a NULL pointer.
int_ptr_base(Iref_Count* ptr);
// Copy constructor
int_ptr_base(const int_ptr_base& r);
// Destructor core
Iref_Count* unassign();
// some more functions...
};
template <class T>
class int_ptr : protected int_ptr_base
{public:
// Store a new object under reference count control
// or initialize a NULL pointer.
int_ptr(T* ptr = NULL) : int_ptr_base(ptr) {}
// Destructor, frees the stored object if this is the last reference.
~int_ptr() { delete (T*)unassign(); }
// Basic operators
T* get() const { return (T*)Ptr; }
T& operator*() const { assert(Ptr); return *(T*)Ptr; }
T* operator->() const { assert(Ptr); return (T*)Ptr; }
// some more functions...
};
int_ptr_base::int_ptr_base(Iref_Count* ptr)
: Ptr(ptr)
{ if (Ptr)
++Ptr->Count; // normally InterlockedInc(Ptr->Count);
}
int_ptr_base::int_ptr_base(const int_ptr_base& r)
: Ptr(r.Ptr)
{ if (Ptr)
++Ptr->Count; // normally InterlockedInc(Ptr->Count);
}
Iref_Count* int_ptr_base::unassign()
{ return Ptr && --Ptr->Count == 0 ? Ptr : NULL; // normally
InterlockedDec(Ptr->Count)
}
class Iref_count // Interface for the intrusive_ptr
{ friend class int_ptr_base; // for access to Count
private:
volatile unsigned Count;
protected:
Iref_count() : Count(0) {}
// You must not call the non-virtual destructor directly.
~Iref_count() {}
};
class Slice;
class Iterator
{ intrusive_ptr<Slice> root;
// some more stuff
};
class Slice : public Iref_count
{ scopec_ptr<Iterator> start;
scopec_ptr<Iterator> stop;
// some more stuff
};
To get this to compile either scoped_ptr or intrusive_ptr have to accept
an incomplete type.
Unfortunately I did not have luck with this so far. Neither Slice nor
Iterator are PODs. And the forward declaration of Slice generates wrong
code in intrusive_ptr at the conversion from Iref_count* to Slice*. The
pointer value is not translated by the offset of Iref_count.
Note that intrusive_ptr is not boost::intrusive_ptr, because that won't
compile on my platform. I wrote something similar (see below). However,
I am unsure whether boost::intrusive_ptr would have defined behavior in
this case, and if it does so, why?
Any Ideas?
The cyclic dependency is really needed from the designs point of view.
/* Abstract non-template base class of int_ptr */
class int_ptr_base
{protected:
Iref_Count* Ptr;
public:
// Store a new object under reference count control
// or initialize a NULL pointer.
int_ptr_base(Iref_Count* ptr);
// Copy constructor
int_ptr_base(const int_ptr_base& r);
// Destructor core
Iref_Count* unassign();
// some more functions...
};
template <class T>
class int_ptr : protected int_ptr_base
{public:
// Store a new object under reference count control
// or initialize a NULL pointer.
int_ptr(T* ptr = NULL) : int_ptr_base(ptr) {}
// Destructor, frees the stored object if this is the last reference.
~int_ptr() { delete (T*)unassign(); }
// Basic operators
T* get() const { return (T*)Ptr; }
T& operator*() const { assert(Ptr); return *(T*)Ptr; }
T* operator->() const { assert(Ptr); return (T*)Ptr; }
// some more functions...
};
int_ptr_base::int_ptr_base(Iref_Count* ptr)
: Ptr(ptr)
{ if (Ptr)
++Ptr->Count; // normally InterlockedInc(Ptr->Count);
}
int_ptr_base::int_ptr_base(const int_ptr_base& r)
: Ptr(r.Ptr)
{ if (Ptr)
++Ptr->Count; // normally InterlockedInc(Ptr->Count);
}
Iref_Count* int_ptr_base::unassign()
{ return Ptr && --Ptr->Count == 0 ? Ptr : NULL; // normally
InterlockedDec(Ptr->Count)
}