Sign in to follow this  
Retro_Alt

Templates And Casting

Recommended Posts

If I have a template class, a base class, a derived class, and a function that takes the template of the base: class Base { }; class Derived : public Base { }; template < typename T > class Pointer { }; void Function ( Pointer < Base > B ) { } int main ( ) { Pointer < Derived > A; Function ( A ); } Is there any way I can make "Function ( A );" work automatically? Is this possible? Thanks! Greg

Share this post


Link to post
Share on other sites
You can do this without drilling into the implementation of your other Pointer template instances by assuming a uniform interface.

Ie:

template<typename T>
class Pointer;

class BasePointer
{
template<typename T>
Pointer<T> create( T* ); // or whatever
};

template<typename T>
class Pointer: public BasePointer
{
T* internal;
friend class BasePointer;
public:
template<typename U>
operator Pointer<U>()
{
BasePointer::create<U>( internal );
}
};



The fundamental difference is that it allows someone to specialise Pointer to use some variable other than T* internal. So long as BasePointer::create< U > can create your Pointers from a U*, it works. (I left that unimplemented).

If your Pointer<T> can be trivially constructed from a T*, then you don't even need the BasePointer step -- but if Pointer<T> is anything like a reference counting pointer, then having it silently either take a reference to a T* or add a reference to a T* is fraught with issues.

Share this post


Link to post
Share on other sites
Awesome!

I think I now have a pretty good reference counter. Any thing that can be counted must inherit "Pointable", but that's nbd. :)

Thanks!
Greg


#pragma once

template < typename T >
class Pointer;

// This is a nice reference counting pointer. Any class that you want to have be
// to be pointed to must publicly inherit Pointable. Then a pointer to the object can be
// made with Pointer<Thing>Name. Note that it must be constructed with the item
// it is pointing to, so if making a dynamic object, use:
// Pointer<Thing>Name = Pointer<Thing>::New (params)

class Pointable
{
template<typename T> friend class Pointer;

unsigned int NumRefs;

void IncRefs ( ) { NumRefs++; }
void DecRefs ( )
{
assert ( NumRefs > 0 );
NumRefs--;
}

public:

Pointable ( )
: NumRefs ( 0 ) {}

// Copy constructor
Pointable ( const Pointable & NewP)
: NumRefs ( 0 ) {}

};

template < typename T >
class Pointer
{
template <typename TT> friend class Pointer;

T * Ref;

public:

Pointer ( )
: Ref ( NULL )
{}

Pointer ( T & obj )
: Ref ( & obj )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( T * obj )
: Ref ( obj )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( Pointer<T> & rVar )
: Ref ( rVar.Ref )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( Pointer<T> * rVar )
: Ref ( rVar->Ref )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( const Pointer<T> & rVar )
: Ref ( rVar.Ref )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( const Pointer<T> * rVar )
: Ref ( rVar->Ref )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

template < typename U >
Pointer ( const Pointer<U> & rVar )
: Ref ( rVar.Ref )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

template < typename U >
Pointer ( const Pointer<U> * rVar )
: Ref ( rVar->Ref )
{ //fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

~Pointer ( )
{ //fprintf(stderr, "%i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
if ( Ref != NULL )
{
Ref->DecRefs ( );
if ( Ref->NumRefs < 1 )
{
//fprintf ( stderr, " DELETED\n" );
delete Ref;
}
}
}

T * operator-> ( ) const
{ return Ref; }

T * GetUnprotectedPointer ( ) const
{ return Ref; }

T & GetUnprotectedRef ( ) const
{ return *Ref; }

Pointer & operator = ( const Pointer<T> * rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "%i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
if ( Ref->NumRefs < 1 )
{
//fprintf ( stderr, " DELETED\n" );
delete Ref;
}
}
Ref = rVar->Ref;
//fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );

return *this;
}

Pointer & operator = ( const Pointer<T> & rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "%i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
if ( Ref->NumRefs < 1 )
{
//fprintf ( stderr, " DELETED\n" );
delete Ref;
}
}
Ref = rVar.Ref;
//fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );

return *this;
}

Pointer & operator = ( const T * rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "%i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
if ( Ref->NumRefs < 1 )
{
//fprintf ( stderr, " DELETED\n" );
delete Ref;
}
}
Ref = rVar;
//fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );
return *this;
}

Pointer & operator = ( const T & rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "%i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
if ( Ref->NumRefs < 1 )
{
//fprintf ( stderr, " DELETED\n" );
delete Ref;
}
}
Ref = &rVar;
//fprintf(stderr, "%i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );
return *this;
}

friend bool operator == ( const Pointer<T> & lhs,
const Pointer<T> & rhs )
{
return ( lhs.Ref == rhs.Ref );
}

template <typename... Args> static
Pointer < T > New (Args&&... params)
{
return Pointer < T > ( new T ( (params)... ) );
}

static
Pointer < T > New ( Pointer < T > Ti)
{
return Pointer < T > ( new T ( *Ti.Ref ) );
}
};

template < typename T >
class Newable
{
public:
template <typename... Args> static
Pointer < T > New (Args&&... params)
{
return Pointer < T > ( new T ( (params)... ) );
}
};



Share this post


Link to post
Share on other sites
Your code isn't remotely thread safe: and that is a rather important property. Even if you don't need it yet, do the make-work to make it possible.

Generally you want your inc and dec refs to return the value of the reference count immediately after you did the add/remove ref.

Then the code that calls add/remove ref takes the return value, and makes a decision based on this.

Second, I would move more of the implementation from the Pointer<> class to the Pointable<> class. Make the Pointer<> class a simple RAII addref/removeref class that isn't in charge of deleting the Pointable<> object.

Make the Pointable<> class have a virtual void DestroySelf() { delete this; } method.

Have int DecRefs() do the following:

int retval = --NumRefs;
if (!retval) {
DestroySelf();
}
assert(retval >= 0);
return retval;


From the above, a fully thread-safe implementation is a mere "substitute operator -- for a thread-safe function" away.

In addition, the DestroySelf() method allows you to create reference counted objects that pool themselves, have a pre-destructor phase, or a number of other tricks. And by default, call delete.

There is even the simple:

template<typename Pointable>
class OnStack: public Pointable {
void DestroySelf()
{
assert(false);
}
public:
virtual ~OnStack()
{
assert(NumRefs == 1);
}
};

which creates an on-stack Pointable object type, in case you need it for whatever reason. :)

Share this post


Link to post
Share on other sites
Okay, I think I have it now that I can add thread locks in the future, whenever that time may come ( I still haven't decided between SDLthreads and pthreads ). I made the changes you recommended and it definitely feels more elegant. I had one bug that was really making me scratch my head, and that was that my objects were being deleted, but the destructors weren't being called, and that was because I didn't have an empty virtual destructor in Pointable!

Do you see anything else that might potentially be a problematic?

Thanks!
Greg


#pragma once

template < typename T >
class Pointer;

// This is a nice reference counting pointer. Any class that you want to have able
// to be pointed to must publically inheirit Pointable. Then a pointer to the object can be
// made with Pointer<Thing>Name. Note that it must be constructed with the item
// it is pointing to, or assigned. If making a dynamic object, use:
// Pointer<Thing>Name = Pointer<Thing>::New (params)

class Pointable
{
template < typename T > friend class Pointer;

unsigned int IncRefs ( ) { return ++NumRefs; }

unsigned int DecRefs ( ) { int retval = --NumRefs;
if (!retval)
{
//fprintf ( stderr, " DELETED\n" );
DestroySelf();
}
assert(retval >= 0);
return retval;
}

virtual void DestroySelf() { delete this; }

protected:
unsigned int NumRefs;

public:

Pointable ( )
: NumRefs ( 0 ) {}

// Copy constructor
Pointable ( const Pointable & NewP)
: NumRefs ( 0 ) {}

virtual ~Pointable ( )
{
assert ( NumRefs == 0);
}
};

template < typename T >
class Pointer
{
template <typename TT> friend class Pointer;

T * Ref;

public:

Pointer ( )
: Ref ( NULL )
{}

Pointer ( T & obj )
: Ref ( & obj )
{ //fprintf(stderr, "& i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( T * obj )
: Ref ( obj )
{ //fprintf(stderr, "* %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( const Pointer<T> & rVar )
: Ref ( rVar.Ref )
{ //fprintf(stderr, "p& %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

Pointer ( const Pointer<T> * rVar )
: Ref ( rVar->Ref )
{ //fprintf(stderr, "p* %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

template < typename U >
Pointer ( const Pointer<U> & rVar )
: Ref ( rVar.Ref )
{ //fprintf(stderr, "pu& %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

template < typename U >
Pointer ( const Pointer<U> * rVar )
: Ref ( rVar->Ref )
{ //fprintf(stderr, "pu* %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( ); }

~Pointer ( )
{ //fprintf(stderr, "~ %i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
if ( Ref != NULL )
Ref->DecRefs ( );
}

T * operator-> ( ) const
{ return Ref; }

T * GetUnprotectedPointer ( ) const
{ return Ref; }

T & GetUnprotectedRef ( ) const
{ return *Ref; }

Pointer & operator = ( T * rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "=* %i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
}
Ref = rVar;
//fprintf(stderr, "=* %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );
return *this;
}

Pointer & operator = ( T & rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "=& %i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
}
Ref = &rVar;
//fprintf(stderr, "=& %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );
return *this;
}

Pointer & operator = ( const Pointer<T> * rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "=p* %i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
}
Ref = rVar->Ref;
//fprintf(stderr, "=p* %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );

return *this;
}

Pointer & operator = ( const Pointer<T> & rVar )
{
if ( Ref != NULL )
{
//fprintf(stderr, "=p& %i-- : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->DecRefs ( );
}
Ref = rVar.Ref;
//fprintf(stderr, "=p& %i++ : %s : %p\n", Ref->NumRefs, typeid(this).name(), Ref);
Ref->IncRefs ( );

return *this;
}

friend bool operator == ( const Pointer<T> & lhs,
const Pointer<T> & rhs )
{
return ( lhs.Ref == rhs.Ref );
}

template <typename... Args> static
Pointer < T > New (Args&&... params)
{
return Pointer < T > ( new T ( (params)... ) );
}

static
Pointer < T > New ( Pointer < T > Ti)
{
return Pointer < T > ( new T ( *Ti.Ref ) );
}
};


template < typename T >
class OnStack: public T, public Pointable
{
void DestroySelf()
{
assert ( FALSE );
}
public:
virtual ~OnStack ( )
{
assert ( NumRefs == 1 );
}
};


Share this post


Link to post
Share on other sites
Your operator= sucks. :)

Here is the one true operator=(tm):

foo& operator=(otherT const& other)
{
foo tmp = other;
this->swap(tmp);
return *this;
}

Well, actually, there are two true operator=s:

foo& operator=(foo const& other)
{
foo tmp = other;
this->swap(tmp);
return *this;
}
template<typename T>
foo& operator=(T const& other)
{
foo tmp = other;
this->swap(tmp);
return *this;
}

Now all you have to write is non-explicit copy constructors and a method swap(foo&) that swaps the content of two instances of foo.

And operator= just works.

Your constructors should be the following:

Pointer() {};
Pointer(Pointer const& other);
template<typename U>
Pointer(Pointer< U > const& other);
template<typename U>
Pointer(U* other);

Your other constructors are a bad idea.


//This operator:
T * operator->() const
//and this operator:
T& operator*() const
//should be implemented in terms of:
T * GetUnprotectedPointer ( ) const
T & GetUnprotectedRef ( ) const



GetUnprotectedPointer should be renamed to "BorrowPointer", because that is what you are doing.

Possibly you should support operator T* (and maybe operator U*) implicit casting. This is useful when you have a function that doesn't know it is taking a reference counted object.
  template <typename... Args> static

C++0x I assume?

OnStack should assume the typed passed in was Pointable.

You might want a T* ClonePointer() method, possibly private. It makes a bunch of the rest of the reference counting code easier.

Another useful one for auto pointer classes is
T*& FillPtr() { Pointer tmp; this->swap(tmp); return Ref; }

which works well with the interfaces that take a T** or the like and want to fill it. But I suppose they can take a Pointer<T>* just as easily.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this