Casting member function pointers from derived class to virtual base class

Hello everyone,



I have no clue why the following does not work!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Base
{

};

class A : public virtual Base
{
public:
      void FunctionA(void)
      {
            printf("void FunctionA(void)");
      };
};

typedef void (Base::*FDoItBase)();
typedef void (A::*FDoItA)();

class B : public A
{
public:
      void FunctionBCallBack(void)
      {
            printf("void FunctionBCallBack(void)");
      };

      void FunctionB(void)
      {
            FDoItA DoItA = (FDoItA)(&B::FunctionBCallBack);   //OK compiles!
            FDoItBase DoItBase= (FDoItBase)(&B::FunctionBCallBack); //Error!
      };
};


int _tmain(int argc, _TCHAR* argv[])
{
     
      return 0;
}



Hope someone can explain why the cast to a direct base class works and why the virtual base class gives errors?

Thanx in advance,

Ponti.
The clue might be in the error. Care to share it?
sure, here it is:

1>x:\fptest\fptest.cpp(35) : error C2440: 'type cast' : cannot convert from 'void (__thiscall B::* )(void)' to 'FDoItBase'
1>x:\fptest\fptest.cpp(35) : error C2440: 'initializing' : cannot convert from 'void (__thiscall B::* )(void)' to 'FDoItBase'
1>        Cast from base to derived requires dynamic_cast or static_cast


and no, it does not require dynamic_cast or static_cast as the error output says. Those are function pointers not object pointers. And besides the cast to the virtual base, the other cast does work.
Here's why.

Consider the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base1 {
    int x;
  public:
    virtual void foo();
};

class Base2 {
    int y;
  public:
    virtual void bar();
};

class Derived : public Base1, Base2 {
    int z;
  public:
    virtual void baz();
};


In memory, the layout of an instance of Derived probably looks something like:


MemAddr Contents
--------------------------------------------------------------------------------
0x10000000 vtable pointer for Derived
0x10000004 Derived::z
0x10000008 vtable pointer for Base1
0x1000000c Base1::x
0x10000010 vtable pointer for Base2
0x10000004 Base2::y


So

 
Derived* d = new Derived;   // d has the address 0x10000000 


So, when the compiler needs to do a vtable lookup of Derived::baz, it
reads the vtable pointer from address 0x10000000, or, in other words,
(*d + 0 ).

And in fact, the compiler knows that given a pointer to an instance of an
object, the vtable pointer is always at offset 0. Furthermore, the first
data member of the object is always at offset 4 (assuming 32-bit vtable
pointer), etc.

But the multiple inheritance case breaks that. In the example above, to
call Base2::bar(), the compiler has to "fix up" the "this" pointer -- ie, d.
It needs to add 16 to it, and NOW the vtable pointer for Base2 is at
offset 0 and Base2::y is at offset 8 again.

The compiler has to do this because the compiler has to assume you
will instantiate Base2 directly, meaning that when it compiles the code
for Base2, it must assume the vtable pointer is at offset 0 and the first
data member, y, is at offset 4.

So why are you getting the compile error? Ultimately, it is because when
functions are called in Base1 and/or Base2 through an instance of D, the
compiler has to generate a "thunk" whose purpose is to "fix up" the "this"
pointer that will get passed to the member function.

At the point at which you call the member function by pointer, the compiler
cannot know which object contains the member function -- Derived, Base1,
or Base2, and therefore it does not know how to generate (or even if it
needs to generate) the fix up code.

(EDIT: My memory layout above may not be 100% accurate).

Last edited on
Thank you for your reply.

I have changed the source a bit to assume multiple inheritance as in your example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "stdafx.h"

#pragma pointers_to_members(full_generality, virtual_inheritance)

class Base
{
public:

};

class A : public  virtual Base
{
public:
	void FunctionA(void)
	{
		printf("void FunctionA(void)");
	};
};

class C 
{
public:
	void FunctionC(void)
	{
		printf("void FunctionC(void)");
	};
};

typedef void (Base::*FDoItBase)();
typedef void (A::*FDoItA)();
typedef void (C::*FDoItC)();

class B : public C, public A
{
public:
	void FunctionBCallBack(void)
	{
		printf("void FunctionBCallBack(void)");
	};

	void FunctionB(void)
	{
 		FDoItA DoItA = (FDoItA)(&B::FunctionBCallBack);//ok
		FDoItC DoItC = (FDoItC)(&B::FunctionBCallBack);//ok
		FDoItBase DoItBase= (FDoItBase)(&B::FunctionBCallBack); //does NOT compile when A is virtual inherited from Base!

		long szA = sizeof(FDoItA);
		long szBase = sizeof(FDoItBase);
		long szB = sizeof(&B::FunctionBCallBack);
		long szC = sizeof(FDoItC);

	};
};


int _tmain(int argc, _TCHAR* argv[])
{
	B b; 
	b.FunctionB();
	return 0;
}



now removing the virtual specifier from class A, the code compiles, but gives warnings about the function pointer sizes not the same.
Using the #pragma resolves these warnings.

adding the virtual base class specifier gives a compile error.

I understand about the offsets when using multiple inheritance, but i'm not convinced that is the problem here.
What I explained is the general reason why member function pointers have to include the class name.

What you are doing may compile with some microsoft magic, but it is definitely non-standard.

You might consider using the boost::function and boost::bind libraries which solve your problem
in a standards-compliant, portable fashion.
Topic archived. No new replies allowed.