Templates And Casting

Started by
5 comments, last by NotAYakk 14 years, 10 months ago
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
Thanks!Greg
Advertisement
The way boost does this for it's smart pointer types is by giving Pointer a constructor ala:

template < typename T > class Pointer {    T* internal;public:    template < typename U > Pointer( const Pointer<U>& other ): internal(other.internal) {}};
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.
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 oncetemplate < 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)... ) );  }};
Thanks!Greg
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. :)
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 oncetemplate < 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 );  }};
Thanks!Greg
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.

This topic is closed to new replies.

Advertisement