March 15, 2004
Hello,
I'm writing an article about member function pointers (MFP's), including a
description of how they are implemented by as many compilers as I can get my
hands on. DMC++ has the most interesting implementation that I've seen.
In DMC, a MFP is the same size as a function pointer, even when multiple
inheritance is involved. On every other compiler, an MFP in this situation is a
struct containing a function pointer + stuff to adjust the 'this' pointer
correctly when dealing with multiple/ virtual inheritance. For a 32-bit system,
an MFP is typically 12 bytes long. But in DMC, it's just 4 bytes long!

How is DMC doing the adjustment to the 'this' pointer without storing it in the MFP? Here's my guess:

For single-inheritance classes, a member function pointer is just the address of
the function. When more complex inheritance is involved, the member function
pointer points to a 'thunk' function, which performs the necessary adjustments
to the this pointer, and then calls the real member function. One of these
little thunk functions is created for every member function that's involved in
multiple inheritance.
The challenge with doing this is to cope with conversions between base and
derived classes. You can cope with this by making each thunk function a standard
size (say 26 bytes). When converting from a derived class to a base class with
(say) 11 member functions, you would subtract 11*26 from the function address.
You only need to do this for functions that are involved in multiple
inheritance. Because the classes involved are known at compile time, the
compiler can calculate the adjustment number (11*26) when compiling.

If this guess is correct, it means that DMC has by far the most efficient implementation of member function pointers. Can anyone on this list confirm or deny this guess?

Cheers,
Don.


March 24, 2004
DMC++ creates a 'thunk' when necessary to adjust the this pointer. The thunk is just a stub of code that offsets ECX then jumps to the real function. I believe VC does this as well. You can see how it does this by running OBJ2ASM on some test code.