none
Compiler bug: virtual inheritance + dynamic_cast causes object pointer shift

    Question

  • Hi

    I'd like to know where can I submit a VC++ compiler bug? Was reproduced with both VC++ 2005 SP1 & VC++ 2008.
    The problem happens in some combination of using virtual inheritance & dynamic_cast. The cast returns pointer with an offset. The same code works fine when compiled with g++ compiler.

    Note the comments in main(): there are 2 print outs - first in a constructor, the second in a member function. The second print out shows a 'this' pointer shift, while it's expected to be the same as in the constructor.

    Output:

    ServiceCreator():_m_testValue=00000000 &m_testValue=0024130C this=00241308
    Create():_________m_testValue=fdfdfdfd &m_testValue=00241304 this=00241300


    Source code:

    #include <iostream>
    #include <iomanip>
    
    using namespace std;
    
    struct IInterface { virtual ~IInterface() {} };
    struct IService : virtual IInterface {};
    struct Service : IService {};
    
    struct IClassCreator : IInterface
    {
        virtual IInterface* Create() = 0;
    };
    
    struct IServiceCreator : virtual IClassCreator
    {
        virtual IService* Create() = 0;
    };
    
    struct ServiceCreator : IServiceCreator
    {
        ServiceCreator() : m_testValue(0)
        {
            cout << "ServiceCreator(): m_testValue=" << hex << setw(8) << setfill('0') << m_testValue
                 << " &m_testValue=" << &m_testValue << " this=" << this << endl;
        }
    
        IService* Create()
        {
            cout << "Create():         m_testValue=" << hex << setw(8) << setfill('0') << m_testValue
                 << " &m_testValue=" << &m_testValue << " this=" << this << endl;
            return new Service();
        }
    
        int m_testValue;
    };
    
    int main()
    {
        // here we will see an output from the constructor:
        // the value of the struct member, its address and pointer 'this'
        ServiceCreator* pServiceCreator = new ServiceCreator();
    
        IClassCreator* pIClassCreator = pServiceCreator;
    
        // next dynamic cast returns fake address (with an offset of 8 bytes downwards)
        IServiceCreator* pIServiceCreator = dynamic_cast<IServiceCreator*>(pIClassCreator);
    
        // Here we expect to see the same value and address
        // as we saw at constructors output, but this does not happen:
        // instead we see that member's address and pointer 'this'
        // was shifted downwards by 8 bytes
        IService* pService = pIServiceCreator->Create();
    
        delete pService;
        delete pServiceCreator;
    
        return 0;
    }
    

    Friday, September 11, 2009 5:48 AM

Answers

All replies

  • Not sure I see what the problem is. 

    Casting, especially in the cases of virtual and multiple inheritance does not guarantee that you will have the same absolute address.  It will simply return the address of the object you request.  If, due to object layout issues, the objects do not start at the same address, then the pointer value will be offset.  Just because the absolute address is different, does not mean that anything is wrong or has behaved incorrectly.

    The actual layout of the objects is completely compiler dependent and should not be expected to be the same from one compiler to another.

    Or is your program crashing and I didn't get that from your post?

    Friday, September 11, 2009 2:19 PM
  • SimonRev, thanks for reply!

    There is something wrong: note, that member field "m_testVal" was initialized to 0 in constructor, and is not supposed to be changed between constructor and the call to "Create" method. But it was - if you run it in debug mode, "m_testVal" will contain "0xfdfdfdfd", also &m_testVal will be shifted by 8 bytes. I don't expect that object pointer will not change, but I do expect, that addresses of member fields will remain constant: otherwise taking the address in a member function would not be valid, which, of cause, is not true.

    Here is an output of the code above:

    ServiceCreator():_m_testValue=00000000 &m_testValue=0024130C this=00241308
    Create():_________m_testValue=fdfdfdfd &m_testValue=00241304 this=00241300


    And answering your question - the program will crash if i'll try to write to m_testVal in method "Create".

    Thanks.
    Saturday, September 12, 2009 3:26 AM
  • >I'd like to know where can I submit a VC++ compiler bug?

    Feedback | Microsoft Connect
    Doug Harrison (Visual C++ MVP)
    Saturday, September 12, 2009 5:36 AM
  • Judging from earlier feedback articles, the combination of covariant return types and virtual base classes is a lethal one.  Most of these are closed as "won't fix", that's what probably will happen to yours.  You can unwedge this problem like this:

      struct IService : /*virtual*/ IInterface {};


    Hans Passant.
    Saturday, September 12, 2009 1:04 PM
  • In this particular case this can be a solution, but in the whole project we have to use "diamonds" inheritance of interfaces at least: since all interfaces derive from IInterface, and some classes implement several interfaces, IInterface will always be inherited virtually. BTW, eliminating covariant return type (making IServiceCreator::Create returning IInterface*) really works around. Is it a common practice to avoid covariant return types and/or virtual inheritance?
    Saturday, September 12, 2009 3:28 PM
  • Judging from the number of feedback articles on this problem, I'd say that very few programmers rely on the combination.  Personally, you'd have to put a gun to my head to make me design a class hierarchy that uses MI with virtual base classes.  A compile option like /vd sounds like what you'll get when you use it.

    Hans Passant.
    Saturday, September 12, 2009 3:58 PM