none
Casting member function pointers from derived class to virtual base class does not work?

    Question

  • Hello everyone,

     

    I have no clue why the following does not work!

    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.

     

     

     

    • Edited by Ponti Thursday, April 22, 2010 9:45 AM inserted code block
    Thursday, April 22, 2010 8:15 AM

Answers

  • Ponti wrote:
    >> Should it print 1, 2, something else?
    > Interesting case. I would like the compiler give ambiguity warnings/errors.
     
    The ambiguity can only be discovered at run-time in this example. pf could point to a member of A, or B, or D, where no ambiguity arises. So a compiler diagnostic is out of the question.
     
    > But i think the solution would be to derive both A
    > and B virtual from Base and initialize Base with f.e. Base(3) in the constructor of D.
     
    The example wasn't supposed to make sense. It was not designed to solve any particular problem, so it's kind of meaningless to provide an alternative solution. I'm just trying to demonstrate that, in general, it's physically impossible for a compiler to pack sufficient information into a pointer-to-member of VirtualBase when pointing to a member of derived class, to make a later call through such a pointer do something reasonable.
     
    Which is why, I guess, the C++ standard prevents this situation from arising in the first place. The standard needs to describe a language that is actually implementable (otherwise we'd just have a compiler with a single DWIM instruction)

    > Even if Base was not derived from a virtual class( = VirtualBase), I think you would have this ambiguity problem.
     
    And, in fact, the same clause in the standard that prohibits casting from a pointer-to-member of derived class to a pointer-to-member of a virtual base class, also prohibits casting to a pointer-to-member of ambiguous base class. From C++03:
     
    4.11p2 An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed.
    (This is about an implicit conversion, with no explicit cast notation - IT).
     
    5.2.9p9 An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1

    --
    Igor Tandetnik
    • Marked as answer by Ponti Monday, April 26, 2010 12:36 PM
    Sunday, April 25, 2010 11:51 PM

All replies

  • 
    Ponti wrote:
    > I have no clue why the following does not work!
    > 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!
    >    };
    > };
    I'm pretty sure you'll find that sizeof(FDoItA) != sizeof(FDoItBase). See
     

    --
    Igor Tandetnik
    Thursday, April 22, 2010 12:13 PM
  • You are correct. 

    sizeof(FDoItA)=12 and sizeof(FDoItBase) = 4

    Base = singe inheritance so 4 bytes. A = virtual inherited so 12 bytes.

    But using #pragma pointers_to_members(full_generality, virtual_inheritance) giving all function pointers 16 bytes, the cast still fails with the same error.

    (also weird is the 16 bytes, shouldn't this be 12 bytes? Using the _virtual_inheritance keyword gives 12 bytes? confusing!)

    I do not understand why the direct derived class cast works and the virtual base class cast fails.:(

     

     

    Thursday, April 22, 2010 2:14 PM
  • actually removing the virtual specifier from the derived A class does compile but gives this error.
    >x:\fptest\fptest.cpp(53) : warning C4407: cast between different pointer to member representations, compiler may generate incorrect code

    This error is related to the size of the pointers. Using the #pragma pointers_to_members(full_generality, virtual_inheritance) resolves these warnings.

     

    Fact is still the cast fails when Base class is virtual inherited. :(

    Thursday, April 22, 2010 2:36 PM
  • 
    Ponti wrote:
    > You are correct.
    > sizeof(FDoItA)=12 and sizeof(FDoItBase) = 4
    > Base = singe inheritance so 4 bytes. A = virtual inherited so 12 bytes.
    > But using #pragma pointers_to_members(full_generality, virtual_inheritance) giving all function pointers 16 bytes, the cast still
    > fails with the same error.
     
    Ah, I see. C-style cast is interpreted as static_cast here, and static_cast from a pointer-to-member of T to a pointer-to-member of a virtual base class of T renders the program ill-formed. You can use reinterpret_cast:
     
    FDoItBase DoItBase = reinterpret_cast<FDoItBase>(&B::FunctionBCallBack);
     
    However, it's not clear what you are trying to achieve here. How do you plan to use those pointers? There's practically nothing you can do with them legally.

    > (also weird is the 16 bytes, shouldn't this be 12 bytes? Using the _virtual_inheritance keyword gives 12 bytes? confusing!)
     
    If I recall correctly, 16 bytes are required when you form a pointer to member of a forward-declared class (I'm not sure why):
     
    class Unknown;
    void (Unknown::*f)();
     
    With #pragma pointers_to_members(full_generality, virtual_inheritance), all pointers have the same size - that of a worst case scenario.
    --
    Igor Tandetnik
    Friday, April 23, 2010 4:44 AM
  • Thank you for your answers,

    Ofcourse the case i showed was just an example equivalent to a real scenario.

    I have the simplified scenario:

    #include "stdafx.h"
    
    //#pragma pointers_to_members(full_generality, virtual_inheritance)
    
    class C;
    class Base
    {
    public:
    	virtual void Read(C& c)
    	{
    		printf("//Read some Base stuff here// \n");
    	};
    };
    
    class A : public virtual Base
    {
    public:
    	void Read(C& c)
    	{
    		printf("//Read some A stuff here// \n");
    
    		Base::Read(c);
    	};
    };
    
    class C; 
    typedef void (Base::*FDoItBase)(C& c);
    
    class C 
    {
    public:
    	void FunctionC(Base& base, FDoItBase fFunction)
    	{
    		printf("//do some C clean stuff here// \n");
    
    		(base.*fFunction)(*this);
    
    		printf("//do some C clean up stuff here// \n");
    	};
    };
    
    
    class B : public A
    {
    public:
    	void FunctionBCallBack(C& c)
    	{
    		printf("//CallBack: do some B clean up stuff here// \n");
    	};
    
    	void Read(C& c)
    	{
    		c.FunctionC(*this, (FDoItBase)(&B::FunctionBCallBack));
    //		c.FunctionC(*this, reinterpret_cast<FDoItBase>(&B::FunctionBCallBack));
    
    		printf("//Read some B stuff here// \n");
    
    		A::Read(c);
    	};
    };
    
    class D : public A
    {
    public:
    	void FunctionDCallBack(C& c)
    	{
    		printf("//do some D cleanup stuff here// \n");
    	};
    
    	void Read(C& c)
    	{
    		c.FunctionC(*this, (FDoItBase)(&D::FunctionDCallBack));
    //		c.FunctionC(*this, reinterpret_cast<FDoItBase>(&D::FunctionDCallBack));
    
    		printf("//Read some D stuff here// \n");
    	
    		A::Read(c);
    	};
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	B* b = new B();; 
    	D* d = new D();
    	C c;
    	
    	Base* base = (Base*)b;
    	base->Read(c);
    
    	base = (Base*)d;
    	base->Read(c);
    
    	return 0;
    }
    

    C only knows Base and vice verse.

    consider C a some kindf of wrapper for the iostream.

    Callback functions are used to inject some cleanup code when the stream encounters f.e. objects from old versions or maybe the filepointer must ignore some bytes (whatever).

    Now the core is the same as the previous example. Same error. When using a virtual base it does not compile . when i remove the virtual specifier everything is ok.

    Using reinterpret_cast together with the pragma everything compiles ok also.

    But i don't know if i'm happy with the reinterpret_cast as  this is considered not safe. (resulting pointer can only being used to cast back to original type).

    (Isn't this implicitly done in this scenario? Meaning does the compiler, when using the function pointer, cast the adress back to the original. The test scenario runs fine with reinterpret_cast.)
    • Edited by Ponti Friday, April 23, 2010 10:05 AM all /br at end of code
    Friday, April 23, 2010 10:03 AM
  • 
    Ponti wrote:
    > Thank you for your answers,
    > Ofcourse the case i showed was just an example equivalent to a real scenario.
    > I have the simplified scenario:
    > #include "stdafx.h"
    >
    > //#pragma pointers_to_members(full_generality, virtual_inheritance)
    >
    > class C;
    > class Base
    > {
    > public:
    >  virtual void Read(C& c)
    >  {
    >   printf("//Read some Base stuff here// \n");
    >  };
    > };
    >
    > class A : public virtual Base
     
    Why do you need virtual inheritance here? You don't have multiple inheritance in your example anywhere. If you drop it, your code should work as is.

    > {
    > public:
    >  void Read(C& c)
    >  {
    >   printf("//Read some A stuff here// \n");
    >
    >   Base::Read(c);
    >  };
    > };
    >
    > class C;
    > typedef void (Base::*FDoItBase)(C& c);
    >
    > class C
    > {
    > public:
    >  void FunctionC(Base& base, FDoItBase fFunction)
    >  {
    >   printf("//do some C clean stuff here// \n");
    >
    >   (base.*fFunction)(*this);
    >
    >   printf("//do some C clean up stuff here// \n");
    >  };
    > };
    >
    >
    > class B : public A
    > {
    > public:
    >  void FunctionBCallBack(C& c)
    >  {
    >   printf("//CallBack: do some B clean up stuff here// \n");
    >  };
    >
    >  void Read(C& c)
    >  {
    >   c.FunctionC(*this, (FDoItBase)(&B::FunctionBCallBack));
    > //  c.FunctionC(*this, reinterpret_cast<FDoItBase>(&B::FunctionBCallBack));
    >
    >   printf("//Read some B stuff here// \n");
    >
    >   A::Read(c);
    >  };
    > };
    I feel your design is unnecessarily complicated. For one thing, what's the role of Base and A, and why do they need to know about C when they never touch it?
     
    How about something like this:
     
    class C {
    public:
      void BeforeRead();
      void AfterRead();
    };
     
    class Base {
    public:
      void Read(C& c) {
        c.BeforeRead();
        DoRead(c);
        c.AfterRead();
      }
    private:
      virtual void DoRead(C& c) {}  // possibly pure virtual
    };
     
    class B : public Base {
      virtual void DoRead(C& c) {
        // Do stuff specific to B
      }
    };
     
    This is called a Template Method pattern, if memory serves.
    --
    Igor Tandetnik
    Friday, April 23, 2010 2:40 PM
  • Thank you for your reply.

    I need virtual inheritance because it represents a real world scenario. This is just a real simple extract of a rather big architecture with Base being the class from which almost all classes are derived. Some multiple inheritance takes place and virtual inheritance is a must.

    It's this virtual Base that's giving problems, because the pointers are not correctly cast.

    I just checked the  same source with C++ Builder. This compiles without any problems!. Here the size of the Base function pointer is 12 (by default). No need for any reinterpret_cast. It feels like Studio does not follow the standard correctly. ( or C++ Builder does not)

    [How can i get the size of the Base function pointer to 12 anyway?. Only 4 (default) or 16 (full_generality) are possible.]

    The BeforeRead and AfterRead are not a bad idea.

    Like this i think:

    void B::Read(C& c)
    {
    	c.FunctionC1(*this);
    
    
        FunctionBCallBack(c); 
    
    
        c.FunctionC2(*this);    
    
    
    	printf("//Read some B stuff here// \n");
    	
        A::Read(c);
    };
    

    Could work, though i don't like splitting FunctionC in two seperate functions (a before and an after). You would not want someone to call a before without an after. This could be dangerous in the current architecture. 

     

    But i still need a (working) function pointer to the Base in other scenarios (not explained here).

    So it would resolve probably this problem but others would still exist.

    • Edited by Ponti Friday, April 23, 2010 11:11 PM code changes
    Friday, April 23, 2010 11:09 PM
  • Ponti wrote:
    > I need virtual inheritance because it represents a real world scenario. This is just a real simple extract of a rather big
    > architecture with Base being the class from which almost all classes are derived. Some multiple inheritance takes place and
    > virtual inheritance is a must. 
     
    Well, then pointers-to-members won't work for you. As far as I can tell, there is no legal way to obtain a value of type pointer-to-member of virtual base class, which acually points to a member of derived class. Consider:
     
    class VirtualBase {};
     
    class Base : public VirtualBase {
    public:
      int x;
      Base(int xx) : x(xx) {}
      void f() { cout << x; }
    };
     
    class A : public Base { A() : Base(1) {} };
    class B : public Base { B() : Base(2) {} };
     
    class D : public A, public B {};
     
    typedef void (VirtualBase::*PF)();
    PF pf = some_cast<PF>(&Base::f);
     
    Imagine that this last cast was possible, somehow. What should happen here:
     
    D d;
    (d.*pf)();
     
    Should it print 1, 2, something else?

    > It's this virtual Base that's giving problems, because the pointers are not correctly cast.
    > I just checked the  same source with C++ Builder. This compiles without any problems!. Here the size of the Base function pointer
    > is 12 (by default). No need for any reinterpret_cast. It feels like Studio does not follow the standard correctly.
     
    No, I believe it's the other way round. By compiling your code without error, C++ Builder doesn't follow the standard correctly. Comeau agrees with VC++.
     
    > [How can i get the size of the Base function pointer to 12 anyway?. Only 4 (default) or 16 (full_generality) are possible.]
     
    I don't know. Perhaps you can't. Why does it matter?
    --
    Igor Tandetnik
    Saturday, April 24, 2010 12:16 AM
  • > Should it print 1, 2, something else?

    Interesting case. I would like the compiler give ambiguity warnings/errors. But i think the solution would be to derive both A and B virtual from Base and initialize Base with f.e. Base(3) in the constructor of D.  I think such a case as this is (among other reasons) why they  have invented the virtual base class.

    I suppose you would then have this situation:

    class VirtualBase {};
     
    class Base : public VirtualBase {
    public:
     int x;
     Base(int xx) : x(xx) {}
     void f() { cout << x; }
    };
     
    class A : public virtual Base { A() : Base(1) {} };
    class B : public virtual Base { B() : Base(2) {} };
     
    class D : public A, public B { D(): Base(3) {} };
     
    typedef void (VirtualBase::*PF)();
    PF pf = some_cast<PF>(&D::f);
    
    D d;
    (d.*pf)();

    Even if Base was not derived from a virtual class( = VirtualBase), I think you would have this ambiguity problem. (and being Base the virtual class should solve it).

     

    > I don't know. Perhaps you can't. Why does it matter?

    Actually it doesn't at all. Just trying something (always the explorer). And VS studio not really giving me control of the size is annoying (but not important).

    Sunday, April 25, 2010 9:42 PM
  • Ponti wrote:
    >> Should it print 1, 2, something else?
    > Interesting case. I would like the compiler give ambiguity warnings/errors.
     
    The ambiguity can only be discovered at run-time in this example. pf could point to a member of A, or B, or D, where no ambiguity arises. So a compiler diagnostic is out of the question.
     
    > But i think the solution would be to derive both A
    > and B virtual from Base and initialize Base with f.e. Base(3) in the constructor of D.
     
    The example wasn't supposed to make sense. It was not designed to solve any particular problem, so it's kind of meaningless to provide an alternative solution. I'm just trying to demonstrate that, in general, it's physically impossible for a compiler to pack sufficient information into a pointer-to-member of VirtualBase when pointing to a member of derived class, to make a later call through such a pointer do something reasonable.
     
    Which is why, I guess, the C++ standard prevents this situation from arising in the first place. The standard needs to describe a language that is actually implementable (otherwise we'd just have a compiler with a single DWIM instruction)

    > Even if Base was not derived from a virtual class( = VirtualBase), I think you would have this ambiguity problem.
     
    And, in fact, the same clause in the standard that prohibits casting from a pointer-to-member of derived class to a pointer-to-member of a virtual base class, also prohibits casting to a pointer-to-member of ambiguous base class. From C++03:
     
    4.11p2 An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed.
    (This is about an implicit conversion, with no explicit cast notation - IT).
     
    5.2.9p9 An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1

    --
    Igor Tandetnik
    • Marked as answer by Ponti Monday, April 26, 2010 12:36 PM
    Sunday, April 25, 2010 11:51 PM
  • I had to google DWIM. Love it. I always look forward to reading your posts, Igor.
    Monday, April 26, 2010 3:47 AM
  • Ok. Thank you very much. Point(s) well taken. :) I suspect the same is true for C++0x. Also good to know VC++ conforms to the standard unlike C++Builder and Intel C++.

    I now solve the issues i have with global functions and function pointers and using friend specifiers. (not really nice but it works.)

     

    Thank you for your patience and help Igor!

    Monday, April 26, 2010 12:35 PM