Syed Babu

Take the following code

class A
{
public:
A()
{
a = 0;
}
virtual void Print()
{
cout<<" a = "<<a<<endl;
}
protected:
int a;

};

//***************************************************************************

class B
{
public:
B()
{
b = 0;
}
virtual void Check()
{
cout<<" This is Check() of class B"<<endl;
}

protected:
int b;

};

//***************************************************************************

class C : public A,public B
{
public:
C()
{
c = 0;
}
virtual void Print()
{
cout<<" c = "<<c<<endl;
}
virtual void Check()
{
cout<<" This is Check() of class C"<<endl;
}
private:
int c;
};

//***************************************************************************

void main()
{
C obj;

A* aPtr = &obj;
aPtr->Print();
B* bPtr = &obj;
bPtr->Check();

}

The class C overrides the virtual function from class A [ Print() ] and the virutal function from class B [ Check() ].

If I create a object for class C [ say obj] the entry point is same for A & C in obj.

But where the entry point for B in obj is quite different ie., the expression (B*)obj does not point to the same part of the class as does (A*)obj.

Since the function C::Check() expects to receive an C* as its hidden this parameter, we should have an adjuster thunk in the vftable of B class in the C object.

But there is no such entry available ib the vftable of B class in the C object why

Then how the mechanism of converting the B* to C* is achieved



Re: Visual C++ Language Adjuster Thunks

einaros

I'm not sure I completely understand what you mean. There's no need to convert a B* to a C*. The compiler knows that C::Check will either be called by a C object / reference / pointer or a B reference / pointer, and address all (inherited) C instance members thereafter. The instance pointer will target the same location within the C object no matter how Check is called -- granted that the call isn't malformed by casts or similar. Since the instance pointer remains the same, no offsets will need adjustment.





Re: Visual C++ Language Adjuster Thunks

Jonathan Caves - MSFT

C::Check does need an this-ptr adjustor thunk and the compiler does provide one:

Code Snippet

class A size(8):
+---
0 | {vfptr}
4 | a
+---

A::$vftable@:
| &A_meta
| 0
0 | &A::Print

A::Print this adjustor: 0


class B size(8):
+---
0 | {vfptr}
4 | b
+---

B::$vftable@:
| &B_meta
| 0
0 | &B::Check

B::Check this adjustor: 0


class C size(20):
+---
| +--- (base class A)
0 | | {vfptr}
4 | | a
| +---
| +--- (base class B)
8 | | {vfptr}
12 | | b
| +---
16 | c
+---

C::$vftable@A@:
| &C_meta
| 0
0 | &C::Print

C::$vftable@B@:
| -8
0 | &C::Check

C::Print this adjustor: 0
C::Check this adjustor: 8






Re: Visual C++ Language Adjuster Thunks

einaros

I was under the impression that the "adjustor thunk" would imply a jump table or offset based on the polymorphic type of the instance pointer, as opposed to the fact that the instance is offset into C, targetting B's base. (The latter of which I tried to explain.)

My bad Smile





Re: Visual C++ Language Adjuster Thunks

einaros

Reconsidering this it occurs to me that the thunk probably refers to the inlined piece of code that adjusts the instance pointer assignment. For some reason my notion of 'thunk' has been a separate code block, such as the vcalls.





Re: Visual C++ Language Adjuster Thunks

Syed Babu

Hi Jonathan

Why I didnt get adjuster thunk in my vtable entry

Is it compiler dependent

I'm using visual studio 6.0

Do we have some setting to do this [ like we have pragma vtordisp to swithc ON and OFF the vtordisp entry ]

I thought as you are with vc++ compiler team it is more appropriate to ask you.





Re: Visual C++ Language Adjuster Thunks

Jonathan Caves - MSFT

First off my usual comment - Visual C++ 6.0 is no longer supported.

I would be surprised that you don't have an adjustor thunk. One way to test this is to call a virtual method which attempts to write out the value of some data member. If you don't have a adjustor thunk then the values should be garbage - if they are valid then you are getting an adjustor thunk.






Re: Visual C++ Language Adjuster Thunks

Syed Babu

class A
{
public:
A()
{
a = 0;
}
virtual void Check()
{
cout<<" This is Check() of class A"<<endl;
}
protected:
int a;

};

class B
{
public:
B()
{
b = 0;
}
virtual void Print()
{
cout<<" b = "<<b<<endl;
}

protected:
int b;

};

class C : public A,public B
{
public:
C()
{
c = 100;
}
virtual void Check()
{
cout<<" This is Check() of class C"<<endl;
}
virtual void Print()
{
cout<<" c = "<<c<<endl;
}

private:
int c;
};

void main()
{
C obj;
B* bPtr = &obj;
bPtr->Print();

}

As you said i called the virtual function which prints the member values.But it works fine.In case of the call bPtr->Print() it calls the version C:Stick out tonguerint() and prints the c. But when i watch the variable obj i dont see any adjuster thunk entry.

I tried in VS 2005 also but I'm getting the same results.





Re: Visual C++ Language Adjuster Thunks

Viorel.

Syed Babu wrote:

[...]

Since the function C::Check() expects to receive an C* as its hidden this parameter, we should have an adjuster thunk in the vftable of B class in the C object.

But there is no such entry available ib the vftable of B class in the C object why

Then how the mechanism of converting the B* to C* is achieved

I do not think C::Check expects a this pointer pointing to C object. It seems the this pointer is always a B object inside C::Check. In order to check it, add a line like this into C::Check:

const C * ptrC = this;

Then investigate the generated Assembler code (use an C/C++ a Output Files compiler option).

Also see how Check is called for C objects:

C obj;

obj.Check();

The Assembler code will include conversion from C * to B *.

I hope this makes sense.





Re: Visual C++ Language Adjuster Thunks

einaros

Viorel. wrote:

The Assembler code will include conversion from C * to B *.

I was under the refined impression that this is what's called the adjustor thunk.






Re: Visual C++ Language Adjuster Thunks

Jonathan Caves - MSFT

Things are not quite as you are assuming - in fact they are, to some extent, the reverse of what you are assuming. As C:: Print overrides B:: Print it is compiled to assume that it always called with pointer to a B.

You can see this if you look at the class-layout of C:

Code Snippet
class C size(20):
+---
| +--- (base class A)
0 | | {vfptr}
4 | | m_a
| +---
| +--- (base class B)
8 | | {vfptr}
12 | | m_b
| +---
16 | m_c
+---

And then look at the disassembly for C:: Print (note to make things clearer I switched to using printf):

Code Snippet

; 51 : printf("In: C::vmf2(): m_b = %d: m_c = %d\n", m_b, m_c);

00007 8b 45 fc mov eax, DWORD PTR _this$[ebp]
0000a 8b 48 08 mov ecx, DWORD PTR [eax+8]
0000d 51 push ecx
0000e 8b 55 fc mov edx, DWORD PTR _this$[ebp]
00011 8b 42 04 mov eax, DWORD PTR [edx+4]
00014 50 push eax

See how m_b is at this+4 and m_c is at this+8 (remember as printf is a __cdecl function the arguments are pushed right to left so m_c is before m_b). These offsets are both relative to a B*.

So when C:: Print is called with B* no thunk is required - but if we call it with a C* then we do need a thunk: so if we look at the disassembly for this function:

Code Snippet

void f2(C* pC)

{

pC->Print();

}

Then we will see the following:

Code Snippet

; 65 : pC->Print();

00023 8b 4d 08 mov ecx, DWORD PTR _pC$[ebp]
00026 83 c1 08 add ecx, 8
00029 8b 45 08 mov eax, DWORD PTR _pC$[ebp]
0002c 8b 50 08 mov edx, DWORD PTR [eax+8]
0002f 8b 02 mov eax, DWORD PTR [edx]
00031 ff d0 call eax

The second instruction is the this-pointer adjustment - it changes the this-pointer from a C* to a B* - which is what C:: Print expects.

This all deeply buried in the C++ compiler and it was all set out in stone back in the early 1990's - before I even joined the Visual C++ compiler team.




Re: Visual C++ Language Adjuster Thunks

Syed Babu

I'm not still very clear about this.

1. If i call the Print function via B* , the pointer has to be converted to C*. Am i right

2. If so how the conversion is happening with out adjuster thunk

If i call the print function via C* as you said we need a adjuster thunk i agree.

3. But there is no adjuster thunk entry in the object lay out at all. Then how the call is getting succeeded





Re: Visual C++ Language Adjuster Thunks

Jonathan Caves - MSFT

1) No you are not correct - see my comments above C:Stick out tonguerint is compiled assuming it alwasy called with a pointer to B*

2) N/A ... see 1) above

3) There is - see the generated assembly above. The thunk is 'inlined' as all it does is adjust the this-pointer.