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.

 

  "Faking" Polymorphism with C++ Templates
  Submitted by



This is a general high-level tip aimed at beginner to intermediate C++ programmers.

Often you'll have a set of datatypes that share a common set of operations. One such example is a polygon. Polygons can take several forms. For instance you might have one polygon class that stores vertices itself (for simplicity - these are useful for "worker" polygons). You might have another polygon class that holds an array of indices into a global vertex store. You may even have yet another polygon class that stores an index into a global buffer of indices into the vertex buffer (this is useful to take advantage of DX8 Index Buffers). In addition, different polygon classes may have different information associated with rendering and so on (texture coordinates, lightmaps, etc).

Now say you want to have a method to clip a polygon against a plane. There are two typical ways to do this in C++. One of which is to implement a separate Clip method for each polygon class. This is not very good because there will be a lot of common code between implementations, and if you find a bug in one you'll have to fix it in all of the others. Another option is to base all the polygons off a common interface, like so:

class IPolygon
{
    virtual int numVerts() = 0;
    virtual Vertex3& getVert(int i) = 0;
    .
    .
    .
};

class IndexedPoly : public IPolygon {...} class WorkerPoly : public IPolygon {...}

void PolyOps_ClipPolygon(Plane* pPlane, IPolygon* pPoly);



While this is a good solution in general, there are several problems with it when you are looking at it from a performance perspective. First of all, the addition of virtual methods adds an additional 4-bytes to the size of each instance. Secondly, the invocation of a virtual method is slower because you have to grab the function address from the vtable instead of calling it by address directly.

C++ templates give you an additional approach, which if used in good measure, can offset these costs. Keep in mind that inheritance from an abstract base class is the preferred way of handling polymorphism, we're only interested in optimizing areas where size and speed really matter, while still being able to take advantage of code reuse.

Consider the following code snippet:

class WorkerPoly
{
    int numVerts();
    Vertex3& getVert(int i);
    .
    .
    .
};

class IndexedPoly { int numVerts(); Vertex3& getVert(int i); . . . };



These two polygon classes, while different, do have the same method signatures. We could take these signatures to provide a generic template function that operates on both datatypes:

template<class T
void PolyOps_ClipPolygon(Plane* pPlane, T* pPoly); 



Note that this increases the compiled code size for each new datatype you add that is used as the template parameter, but since you probably only have a handful of different types of polygons at most, this is negligible. The advantage is that now you have the code to split a polygon in one place, yet do not have any additional runtime overhead.

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.