Compile-time virtual function calls in C++

21 August, 2009 § 7 Comments

C++ templates provide some really fascinating abilities, one of which is compile-time virtual function calls. WTL, the Windows Template Library, uses compile-time virtual function calls when deriving types. In this post, I will cover what I mean when I say “compile-time virtual function calls”, and how they create faster code and smaller executables.

By far, below is the most common use of templates within C++ code:

namespace std {
   template <typename T>
   class vector {
      /* ... */
   }
}
std::vector<int> primeNumbers;

But what if we wanted to derive our own type, with a templated class being the base type? See this example:

template <typename T>
class WindowImpl {
   int id_of_windowImpl;
   static LPCTSTR GetWindowCaption() { return NULL; }
   void DrawWindow() {
      LPCTSTR windowTitle = T::GetWindowCaption();
      /* Draw window and place title at top of UI */
   }
} 

Did you notice that there was no mention of the keyword virtual? This is where our little trick comes in. Let’s say we wanted to make our own boring derivation of Window, one that has the title of “Basic Window”. We could simply do this:

class BasicWindowImpl : WindowImpl<BasicWindowImpl> {
   int id_of_basicWindowImpl;
   static LPCTSTR GetWindowCaption() {
      return _T("Basic Window");
   }
}

BasicWindowImpl<BasicWindowImpl> basicWindow;

When the code is compiled, the static method GetWindowCaption will be called on the derived type. If the method isn’t implemented in the derived type, then the C++ lookup rules state that it will look deeper in the class and find the base implementation that returns NULL.

Also, this whole practice is made possible since immediately following the colon, the type has been declared and the typename can be used as a template parameter.

What does this mean in terms of efficiency/footprint?

First a short high level overview on how virtual functions are implemented. When a method inside of a class is declared virtual, a special data type is added to the class. This data type stores function offsets from the base of the class. When a call is made using a base type, this virtual function table (vftbl or vtable for short) is referenced and the memory address of the function is retrieved and executed.

Each instance of a class will hold a pointer to this table (often called a vpointer). This pointer is usually initialized at the end of the constructor by some hidden code that your C++ compiler generates.

On a 32-bit machine with 32-bit ints and no virtual functions: basicWindow is 8 bytes (4 bytes for each int that it holds).

On a 32-bit machine with 32-bit ints and virtual functions: basicWindow is 12 bytes (the same 8 bytes plus 4 bytes for the vpointer). Also, there is another 4 bytes used for the vtable and it’s one function pointer that is stored and pointing to BasicWindowImpl::GetWindowCaption.

In summary, by using compile-time virtual function calls, the sample code cuts it’s memory use in half. The vtable pointer does not consume much memory, but if there were 1,024 BasicWindowImpl instances created, then an extra 4kb of data would be unnecessarily used. Also, there is no need to look up the function address in the table due to the static linking of the overriden function at compile-time.

This brings you a smaller and faster executable by making your compiler work just a little harder.

Where Am I?

You are currently browsing entries tagged with wtl at JAWS.