Adding Virtual Tables to Classes after the Fact

Adding Virtual Tables to Classes after the Fact

Sometimes you want to create classes that are meant to be low level and fast. You don’t want to pay the cost of virtual virtual functions – you want the functions to be inline. But then at other times you wish they could be virtual.

Here is one way to do this.

Consider a set of classes with the same interface. In this case they are mutex implementations:

class MutexOne
{
public:
   void lock        () { /* implementation inline */ }   
   bool tryLock     () { /* implementation inline */ }  
   void unlock      () { /* implementation inline */ }
   void reset       () { /* implementation inline */ }

   static const char* implementationName() { return "MutexOne"; }
};

class MutexTwo
{
public:
   void lock        () { /* implementation inline */ }   
   bool tryLock     () { /* implementation inline */ }  
   void unlock      () { /* implementation inline */ }
   void reset       () { /* implementation inline */ }

   static const char* implementationName() { return "MutexTwo"; }
};

// ... etc ...

All the mmutexes have the same api. Within a testing framework I wish they were virtual so that I can do something like:

testMutexImplementation( MutexVirtual& mutex )
{
    mutex.lock();  // etc... do a lot of complex validating
}

So within the testing framework I wish I had a nice clean virtual base class interface. But in production I don’t want to pay for a virtual function on every mutex operation many of which are like 2-3 opcode in the general case.

You can use templates to add a virtual table after the fact, only when its needed.

First create the desired virtual base class:

class MutexVirtual
{
public:

   virtual void lock        () = 0;
   virtual bool tryLock     () = 0;
   virtual void unlock      () = 0;
   virtual void reset       () = 0; 

   virtual const char* implementationName() = 0;
};

And then use template magic to fill in the virtual table:

template < typename T > class MutexPostVirtual : public MutexVirtual, public T
{
public:
   void lock           () { T::lock(); }
   bool tryLock        () { return T::tryLock(); }
   void unlock         () { T::unlock(); }
   void reset          () { T::reset(); } 

   const char* implementationName() { return T::implementationName(); }
};

It works pretty well. You can use it like this for example:

MutexPostVirtual<MutexOne> mutex1;
MutexPostVirtual<MutexTwo> mutex2;

testMutexImplementation( mutex1 );
testMutexImplementation( mutex2 );

Its possible in some cases to use templatized functions instead of virtual base classes. For example in this case I could have made testMutexImplementation a templatized function:

template < typename T > testMutexImplementation( T& mutex ) { ... }

So long as the mutex classes have the same interface, the compiler will create instances of this function per class. That would be enough if everything inside of testMutexImplementation was simple. But sometimes there really is no substitute for virtual classes. For example if the you need to cascade down a series of functions you’ll have to templatize them all. And sometimes you have to go through functions that can’t be templatized – such as the functions called when you spawn a thread.