Template RefCountPointer Cast/Polymorphism Problem

Started by
8 comments, last by ZuBsPacE 16 years, 3 months ago
Hi there, I'm playing around with a custom Reference Counted Pointer class and ran into a problem.

RefCountPointer< T > 
           |
           v
    RefCountData< T >
           ^
           |
RefCountPointer< T >

Each RefCountPointer instance holds a pointer to the same RefCountData instance, which stores a count variable and a raw pointer to the encapsulated object. So far so good, but I want to extend this to allow polymorphism like this:

RefCountPointer< Derived > pDerived( new Derived() );
RefCountPointer< Base > pBase( pDerived );

Now within "RefCountPointer< T >::RefCountPointer( RefCountPointer< U > pIn )" i need to somehow cast the RefCountData< U > to a RefCountData< T > to work properly, but I'm running out of ideas right now.. Would be nice if someone of you could push me into the right direction.. Here's a short example illustrating my current attempt which doesn't work (as far as I can tell reinterpret_cast messes around with the vtable):

struct Base1
{
	virtual ~Base1() {};

	virtual void Test1() = 0;
};


struct Base2
{
	virtual ~Base2() {};

	virtual void Test2() = 0;
};


struct Derived : public Base1, public Base2
{
	Derived() : Base1(), Base2() {};

	virtual ~Derived() {};

	virtual void Test1() {};
	virtual void Test2() {};
};



template< typename T >
struct Container
{
	T*				m_Ptr;
};



int main( int, char** )
{
	Derived* pDerived = new Derived();
	
	// Following Method Calls Work
	pDerived->Test1();	
	pDerived->Test2();


	// UpCast Of pDerived To A Base1 Pointer
	Base1* pBase1 = pDerived;

	// Following Method Call Works
	pBase1->Test1();


	// UpCast Of pDerived To A Base1 Pointer
	Base2* pBase2 = pDerived;

	// Following Method Call Works
	pBase2->Test2();


	// Up Until Now Everything Works As Expected, Now I'm Trying To Do
	// The Same, But Instead Of Using Raw Pointers, I Encapsulate Them
	// In The Template-Container.


	// Encapsulating pDerived In A Container
	Container< Derived >* pContainerDerived = new Container< Derived >();
	pContainerDerived->m_Ptr = pDerived;

	// Following Method Calls Work
	pContainerDerived->m_Ptr->Test1();	
	pContainerDerived->m_Ptr->Test2();	


	// Brute Force Cast Of The Derived Container To A Base1 Container.. Is There Another Way?
	Container< Base1 >* pContainerBase1 = reinterpret_cast< Container< Base1 >* >( pContainerDerived );

	// Following Method Call Works
	pContainerBase1->m_Ptr->Test1();


	// Brute Force Cast Of The Derived Container To A Base2 Container .. Is There Another Way?
	Container< Base2 >* pContainerBase2 = reinterpret_cast< Container< Base2 >* >( pContainerDerived );

	// ERROR! Following Method Calls Test1()... =[
	pContainerBase2->m_Ptr->Test2();
	

	// Clean Up The Mess;
	delete pContainerDerived;
	delete pDerived;

	return 0;
}


----------------------#include "signature.h"
Advertisement
Why is RefCountData< T > templated? It's an internal data structure for which you can ensure type safety in RefCountPointer.

It's just a std::pair<void *, unsigned int> type of tuple. It doesn't care about types, only about the count.
Hi, are you sure it calls Test1 instead of Test2 ? I'm not sure, but maybe the compiler makes no difference between 2 empty methods, both void and no parameters. Have you tried with some code in those methods ?
About the cast, I can't find any problem with it. But I think a template constructor for RefCountData could do it :
template<class T>;class RefCountData{...template<class U>; RefCountData(RefCountData<U>& other){ // the cast comes here }...}

so you should be able to do this :
Container<Base2>* pContainerBase2 = pContainerDerived;

Maybe I'm all wrong...
Antheus' suggestion interacts badly with multiple inheritance. However, there's a lot of truth in what he says: give your class a pointer to the stored object and a pointer to a shared reference count integer. This is equivalent to adding a a global reference field with another level of indirection to your RefCountData object.
Add a conversion operator for implicit casts.
template< typename T >struct Container{	T* m_Ptr;template<typename T1>Container(Contaner<T1>& rhs):m_Ptr(rhs.m_Ptr){}};Container< Base2 > pContainerBase2(pContainerDerived);

I dont really understand why you are using pointers to Container classes, you also want to look at overloading operators such as "=" "->" "*" "bool" etc.
Hi again, thx for all your replies.


Quote:Original post by ToohrVyk
Antheus' suggestion interacts badly with multiple inheritance. However, there's a lot of truth in what he says: give your class a pointer to the stored object and a pointer to a shared reference count integer. This is equivalent to adding a a global reference field with another level of indirection to your RefCountData object.

If I understood you correctly, you suggest adding T* as a member to each RefCountPtr<T> instance. This way it's possible to un-template RefCountData which is shared.

A very simple solution. I got it working below. I still need a reinterpret_cast, but I guess it's safe if there's no vtable involved.

Right now this will get me going. I think i'll have to look into boost::shared_ptr later on to find out how they did it; adding a ptr-member to each RefCountPtr instance seems like redundant data.



First the Non-Working example:
#include <iostream>template< typename T > class RefCountPtr{	template< typename T >	class RefCountData;        // Private Inner Class Forward Declaration	template< typename U >	friend class RefCountPtr;  // Enable Access To RefCountData* Of Other RefCountPtr Typespublic:	RefCountPtr( T* ptr )		:	m_pRefCountData( new RefCountData< T >( ptr ) )	{		m_pRefCountData->AddCount();	}	RefCountPtr( const RefCountPtr< T >& in )		:	m_pRefCountData( in.m_pRefCountData )	{		m_pRefCountData->AddCount();	}	template< typename U >	RefCountPtr( const RefCountPtr< U >& in )	{		// Getting Rid Of The const...		RefCountPtr< U > constHack( in );		// Evil Cast		m_pRefCountData = reinterpret_cast< RefCountData< T >* >( constHack.m_pRefCountData );		m_pRefCountData->AddCount();	}	~RefCountPtr()	{		m_pRefCountData->RemoveCount();	}	T* operator->() const	{		return m_pRefCountData->GetPtr();	}	T& operator*() const	{		return *( m_pRefCountData->GetPtr() );	}private:	RefCountData< T >*		m_pRefCountData;private:	template< typename T >	class RefCountData	{	public:		RefCountData( T* ptr )			:	m_Ptr( ptr ), m_nCount( 0 )		{		}		void AddCount() 		{ 			++m_nCount;		};		void RemoveCount() 		{			if( --m_nCount == 0 )			{				delete m_Ptr;				delete this;				// "delete this" Works If The DTor Is Empty				// And If "this" Won't Be Accessed Afterwards			}		};		T* GetPtr()		{			return m_Ptr;		};	private:		T*	        m_Ptr;		unsigned int	m_nCount;	};};struct Base1{	virtual ~Base1() {};	virtual void Test1() = 0;};struct Base2{	virtual ~Base2() {};	virtual void Test2() = 0;};struct Derived : public Base1, public Base2{	Derived() : Base1(), Base2() {};	virtual ~Derived() {};	virtual void Test1() { std::cout << "Test1\n";};	virtual void Test2() { std::cout << "Test2\n";};};int main( int, char** ){	// Ok	RefCountPtr< Derived > pDerived( new Derived() );	pDerived->Test1();	pDerived->Test2();	// Ok	RefCountPtr< Base1 > pBase1( pDerived );	pBase1->Test1();	// Error, Calls Test1() !!	RefCountPtr< Base2 > pBase2( pDerived );	pBase2->Test2();	// Output:	//	// Test1	// Test2	// Test1	// Test1	return 0;}



Now the Working example:
- RefCountData now only holds the counter, no template needed.
- each RefCountPointer holds a copy of the pointer to the encapsulated object
#include <iostream>template< typename T > class RefCountPtr{	class RefCountData;          // Private Inner Class Forward Declaration	template< typename U >	friend class RefCountPtr;    // Enable Access To RefCountData* Of Other RefCountPtr Typespublic:	RefCountPtr( T* ptr )		:	m_pRefCountData( new RefCountData() ), m_Ptr( ptr )	{		m_pRefCountData->AddCount();	}	RefCountPtr( const RefCountPtr< T >& in )		:	m_pRefCountData( in.m_pRefCountData ), m_Ptr( in.m_Ptr )	{		m_pRefCountData->AddCount();	}	template< typename U >	RefCountPtr( const RefCountPtr< U >& in )	{		// Getting Rid Of The const...		RefCountPtr< U > constHack( in );		// Evil Cast, but i guess it's safe now		m_pRefCountData = reinterpret_cast< RefCountPtr< T >::RefCountData* >( constHack.m_pRefCountData );		m_Ptr = in.m_Ptr;		m_pRefCountData->AddCount();	}	~RefCountPtr()	{		if( m_pRefCountData->RemoveCount() == 0 )		{			delete m_pRefCountData;			delete m_Ptr;		}	}	T* operator->() const	{		return m_Ptr;	}	T& operator*() const	{		return *( m_Ptr );	}private:	T*			m_Ptr;	RefCountData*		m_pRefCountData;private:	class RefCountData	{	public:		RefCountData()			:	m_nCount( 0 )		{		}		unsigned int AddCount() 		{ 			return ++m_nCount;		};		unsigned int RemoveCount() 		{			return --m_nCount;		};	private:		unsigned int	m_nCount;	};};struct Base1{	virtual ~Base1() {};	virtual void Test1() = 0;};struct Base2{	virtual ~Base2() {};	virtual void Test2() = 0;};struct Derived : public Base1, public Base2{	Derived() : Base1(), Base2() {};	virtual ~Derived() {};	virtual void Test1() { std::cout << "Test1\n";};	virtual void Test2() { std::cout << "Test2\n";};};int main( int, char** ){	// Ok	RefCountPtr< Derived > pDerived( new Derived() );	pDerived->Test1();	pDerived->Test2();	// Ok	RefCountPtr< Base1 > pBase1( pDerived );	pBase1->Test1();	// Ok!! :D	RefCountPtr< Base2 > pBase2( pDerived );	pBase2->Test2();	// Output:	//	// Test1	// Test2	// Test1	// Test2	return 0;}


----------------------#include "signature.h"
You could also choose to inherit RefCountData<T> from a shared RefCountDataBase class that is independent of T and implements the basic "reference-count" protocol and self-destructs when applicable:

namespace impl {class RefCountDataBase {  int refs;public:  RefCountDataBase() : refs(1) {}  void AddCount() { ++refs; }  void RemoveCount() { if (--refs == 0) delete this; }  virtual ~RefCountDataBase() {}};template&lt;typename T&gt;class RefCountData : public RefCountDataBase {  std::auto_ptr&lt;T&gt; data;public:  RefCountData(T *ptr) : data(ptr) {}};} // namespace impl


Note that I dropped the "get pointer" member, because you don't need it (the pointer is already available in your pointer classes).
Is reinterpret_cast even correct here? Wouldn't dynamic_cast (or static_cast, if type is known at compile time) be more appropriate?

Doesn't reinterpret_cast simply mean "treat X bits as Y bits"? While dynamic_cast deals with all the inheritance checking and whatnot?

throw table_exception("(? ???)? ? ???");

Hello ZuBsPacE,

Moving the definition of RefCountData out of the templated class RefCountPointer would allow you to lose the reinterpret_cast from your second example (in your most recent post). This could then be placed: at file scope or inside a namespace, For example: namespaced to avoid adding to the global namespace.

namespace RefCountPointerImpl{  class RefCountData   {    ...  };}template< typename T > class RefCountPtr{  typedef RefCountPointerImpl::RefCountData RefCountData;  ...};


Cheers.

Tom
Thank you all for your wonderful suggestions :)

@ToohrVyk: I like your idea, looks very clean and allows me to get rid of the reinterpret_cast!

@ravyne2001: yeah, reinterpret_cast should be avoided if possible. Now I realized that it's cleaner to make unrelated template classes inherit from a common parent class which provides the interface, if possible. dynamic_cast could probably simplify the process, but I'm trying to avoid enabling RTTI.
----------------------#include "signature.h"

This topic is closed to new replies.

Advertisement