See what's going on with flipcode!

This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.


  Template Overloading For Base Class Pointers
  Submitted by

Let's say you had a template function which took as a parameter a pointer to a type. Now let's say you wanted to specialize a template function for a specific type of pointer. You'd do it like such:

// Forward declaration
class B;

// Generic template function template<typename T bool MyFunc(T *t) { return false; }

// Specialized template function for B template< bool MyFunc(B *t) { return true; }

This will work great if you call MyFunc with an actual pointer to a B object, but what happens when you have a class derived from B (class D) and call MyFunc with its pointer?

// derived class:
class D : public B
{    // definition

void CallingCode( D *d ) { assert( MyFunc(d) ); }

The code WILL throw the assertion because the template compiler resolves the function call to our generic template function version instead of the specialized version. It looks to me that the compiler looks for specializations of the exact type and not types that can be converted to. I don't know if this behaviour complies to the C++ standard or not, but I for one think that it would be useful if you could use polymorphism with templates.

So now we know the problem, here's the solution. The use of templates implies that we'd like to be able to overload a function for any given type. In C++ we can do this without templates by using ellipsis.

bool IsOfTypeFunc(...)
{    return false;

This function will accept all arguments of any type and even of any number of parameters. Of course, use of the ellipsis is inherently type unsafe and just plain dangerous, but we'll just ignore this for now. We can now overload IsOfTypeFunc with a version that takes a pointer to B:

bool IsOfTypeFunc(B *t)
{    return true;

Unlike templates, calling IsOfTypeFunc with a derived B class pointer will resolve to our base class overloaded version. This adheres to the rules of normal function overloading except that the function with the ellipsis acts as a "catch all" for any unresolvable type. So now we can write our calling code as:

void CallingCode( D *d )
    if ( IsOfTypeFunc(d) )
        MyFunc( (B*)d );
        MyFunc( d );

Well now, that if else statement is pretty ugly, but at least it should work now right? Or will it? The code will execute correctly and the second function will indeed never be called during runtime, however the problem here is that the compiler will still replace the second MyFunc call with the generic template code. As a result, if we created the specialization to overcome a compiling error when typename T is replaced with B, we'll still be generating that errorous code. For example:

// Only expecting built-in types for this func (stupid design but this is just for illustrative purposes)
template< typename T  bool MyFunc( T *t )
    char achar = static_cast<char(*t);    //Compiler error if T cannot be converted to char
    return achar;

As a result, the calling code will never compile. So what we'd like to do is have to only call the function we want, period. Well, let's go back to using templates!

// Revised generic template function
template< typename T     bool SolutionFunc( T *t, ... )
    return false;

// Pseudo-specialized version for pointers to objects of type B template< typename T bool SolutionFunc( T *t, B *b) { return true; }

// Helper function to foward on request template< typename T bool ForwardingFunc( T * t ) { return SolutionFunc( t, t ); }

void CallingCode(D *d) { assert( ForwardingFunc(d) ); // THIS WILL WORK! }

Not only do we generate the correct function call, it is the only function call that is generated. We've also gotten rid of that ugly if else statement, plus we now have access to the real object and it's type in the ellipsis function in a compiler enforced, type safe way. And of course, the purpose of this little exercise - using templates with base class pointers - also works! It doesn't get much better than that.

Notes: This code was tested successfully with VC++ 6.0. Props to Gerard Lynch for discovering this stuff in the first place.

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.


Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.