views:

199

answers:

4

Lets have this situation (in c++, in c# classes A,B are interfaces):

class A { virtual void func() = 0; };
class B { virtual void func() = 0; };
class X: public A, public B { virtual void func(){ var = 1; } int var;};

X * x = new X; // from what I know, x have 2 vtables, is this the same in c#?
A * a = (A*)x; // a == x
B * b = (B*)x; // here b != x, so when calling b->func(), how is the address of var correct?

Does the c# compiler create always one vtable? Does it make any pointer fixups when casting?

+1  A: 

Not to be overly pedantic, but the C# compiler does not get involved at this level. The Entire type model, inheritance, interface implementation etc. is actually handled by the CLR more specifically the CTS (Common Type System). .NET compilers mostly just generate IL code that represents intent which is later executed by the CLR where all Vtable handling etc. is taken care of.

For some detail on how the CLR creates and manages runtime types the following link will be a good starting point. Towards the end the MethodTable and Interface Maps are explained. http://msdn.microsoft.com/en-us/magazine/cc163791.aspx

Chris Taylor
+1  A: 

Yes, there is only ever one v-table in a managed language, the CLR does not support multiple inheritance. There is a pointer fixup when you cast to an implemented interface.

This is a notable problem when trying to declare a COM interface that is itself declared from another interface beyond IUnknown. An issue not quite understood by this article's author. COM requires a separate v-table for each interface, just what a compiler that supports MI does.

Hans Passant
If there is only one vtable, how c# supports diamond interface inheritance?
chris
@Chris, if you take a look at the link I provided in my answer you should get a basic idea of how this works. Here is a quote "The duplication of slots is necessary to create an illusion that each interface has its own mini vtable. However, the duplicated slots point to the same physical implementation."
Chris Taylor
The diamond causes trouble because it becomes ambiguous which *implementation* to use. An interface has no implementation. There can only be one base class.
Hans Passant
+2  A: 

If I study this derived version with g++

class X: public A, public B { 
   unsigned magic;
 public:
   X() : magic(0xcafebabe) {};
   virtual void func(){ var = 1; } int var;
};

extern "C" int main() 
{
   X * x = new X; // from what I know, x have 2 vtables, is this the same in c#?
   A * a = (A*)x; // &a == &x
   B * b = (B*)x; // here &b != &x, so when calling b->func(), how is the address of var correct?
   printf("%p -- %p -- %p\n", x, a, b);

   unsigned* p = (unsigned*)((void*) x);
   unsigned *q = (unsigned*)(p[1]);
   printf("x=[%x %x %x %x]\n",p[0],p[1],p[2],p[3]);
   p = (unsigned*)(p[0]);
   printf("a=[%x %x %x %x]\n",p[0],p[1],p[2],p[3]);
   printf("b=[%x %x %x %x]\n",q[0],q[1],q[2],q[3]);

}

It turns out that, in C++ b == a+1, so the structure for X is [vtable-X+A][vtable-B][magic][var] inspecting deeper (nm ./a.out), vtable-X+a contains the reference towards X::func (as one would expect). when you casted your X into B, it adjusted the pointers so that the VTBL for B functions appears where the code expects it.

Did you actually intend to "hide" B::func() ?

B's vtbl looks like holding a reference towards a "trampoline" to X that restores the object pointer to a full X before calling the "regular" X::func that X+A vtbl holds.

080487ea <_ZThn8_N1X4funcEv>:   # in "X-B vtbl"
_ZThn8_N1X4funcEv():
 80487ea:       83 44 24 04 f8          addl   $0xfffffff8,0x4(%esp)
 80487ef:       eb 01                   jmp    80487f2 <_ZN1X4funcEv>
 80487f1:       90                      nop

080487f2 <_ZN1X4funcEv>:        # in X-A vtbl
_ZN1X4funcEv():
 80487f2:       55                      push   %ebp
 80487f3:       89 e5                   mov    %esp,%ebp
 80487f5:       8b 45 08                mov    0x8(%ebp),%eax
 80487f8:       c7 40 14 01 00 00 00    movl   $0x1,0x14(%eax)
 80487ff:       5d                      pop    %ebp
 8048800:       c3                      ret    
sylvainulg
A: 

vtables are an implementation detail. There is no official/required/expected implementation. Different compiler vendors can implement inheritance differently.

James Curran