1

I know that if class A implements for example IUnknown and IComSomething1, than the VTable's method order will be first IUnknown and than IComSomething1. As far as I know this is always the order because IComSomething1 inherits IUnknown.

But what is the order if class A implements IUnknown and IComSomething1 and IComSomething2 ? What is the order of the VTable method pointer between IComSomething1 and IComSomething2 ?

(The question does not apply only to COM, but it comes out of a COM issue that it calls the function by a vtable offset).

I tried to find some answers "out there" but I couldn't find any straight, clear, answer.

Thanks! :-)

TCS
  • 5,790
  • 5
  • 54
  • 86
  • 3
    There is an old article about VC++'s implementation [here](http://www.openrce.org/articles/files/jangrayhood.pdf), I would assume that the basic principles haven't changed (but of course other implementations will be different). – Jesse Good Mar 22 '12 at 21:04
  • 1
    Why does it matter? vtables are implementation details. The C++ standard has no notion of a vtable. – fredoverflow Mar 22 '12 at 21:31
  • 1
    @FredOverflow Sadly, COM object interfaces map to their vtable implementation. It is probably only interesting as a COM question. – Tom Kerr Mar 22 '12 at 21:53
  • 3
    probably the same answer that this question received: http://stackoverflow.com/questions/4608924/memory-layout-difference-between-nested-classes-and-multiple-inheritance-in-c – Tom Kerr Mar 22 '12 at 21:57
  • @FredOverflow it matters to people who like to know how things work underneath. Not for any practical reason (besides reverse engineering, which is very interesting in itself). – Seth Carnegie Mar 23 '12 at 05:03
  • @Jesse - I would mark it as a answer if it wasn't a comment. That article explains my question. – TCS Mar 23 '12 at 05:08

1 Answers1

4

The order of vtable members in a class is not well-defined. Indeed, you can (and will!) find data members between vtable pointers. If you're writing COM, you should cast to whatever interface you want to return in your QueryInterface prior to writing it to the pointer you're putting the result in. That is, something like:

HRESULT QueryInterface(REFIID riid, LPVOID *ppvObject) {
  if (*riid == IID_MY_INTERFACE) {
    *ppvObject = static_cast<IMyInterface *>(this);
    return S_OK;
  } else if (*riid == IID_SOMETHING_ELSE) {
    *ppvObject = static_cast<ISomethingElse *>(this);
    return S_OK;
  } else /* ... */
}

The compiler will take care of finding the right offset for the interface for you.

As for how it actually works, think about what this implies - each of the interfaces must exist as an object at some subrange of offsets within the object.. Say you have a class heirarchy that looks like this:

class IA { virtual void foo() = 0; int x;};
class IB { virtual void bar() = 0; int y; };
class C : public IA, public IB { int bar; };

Your in-memory layout might look like this:

00000000 vtable ptr for C
00000004 vtable ptr for Ia
00000008 int x
0000000b vtable ptr for Ib
00000010 int y
00000014 int bar

Here you can get a pointer to an Ia by getting an offset 0x00000004 into the class, or Ib at 0x0000000b.

It may in some cases by possible to optimize this further:

00000000 vtable ptr for C and Ia
00000004 int x
00000008 vtable ptr for Ib
0000000b int y
00000010 int bar

I'm not sure if the win32 C++ ABI actually does this. If you do do this, then the C vtable starts with the same members as the Ia vtable, then adds on extra ones at the end.

bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • 1
    There's also an intermediate optimization, where the `C` vtable is shared with the `Ia` vtable, but there are two pointers to it. That can be done independently of the ABI and still gives you better locality of reference. – MSalters Mar 23 '12 at 08:38
  • @MSalters, indeed - you may be able to merge the `B` vtable as well in that manner. – bdonlan Mar 24 '12 at 02:23