3

I have been reading around how dynamic_cast works and from what I gathered so far, it obtains the object, gets the vptr, goes to the vtable and in the -1 or 0th element, there is a pointer to a type_info object. However, from this point on it gets a little hazy:

Does the type_info object contain all data necessary to (possibly) do the cast, or does the runtime system need to access other type_info objects?

I am trying to understand how many different objects/vtables and type_info objects are accessed whilst checking an inheritance hierarchy during dynamic_cast.

EDIT: Compiler-wise MSVC or GCC

intrigued_66
  • 16,082
  • 51
  • 118
  • 189
  • You should precise the compiler (visual studio i guess) as it is compiler specific. Also use pointers in your sample code ;) – Drax Jul 30 '14 at 09:10

1 Answers1

0

It's unspecified, and depends on the implementation, but the information relative to converting a pointer to another type in the hierarchy, at least when virtual inheritance is involved, depend on the actual most derived class; having found that the class is a D, information from B or C would not help finding the actual address of A in the D. The position of the A part in the object will differ depending on the most derived class. To see this clearly, create classes with a data member, and display the different addresses:

struct B
{
    int b;
    B() : b( 1 ) {}
    virtual ~B() = default;
};

struct L : virtual public B
{
    int l;
    L() : l( 2 ) {}
};

struct R : virtual public B
{
    int r;
    R() : r( 3 ) {}
};

struct D: public L, public R
{
    int d;
    D() : d( 4 ) {}
};

struct E : public D
{
    int e;
    E() : e( 5 ) {}
};

template <typename T>
class HexDump
{
    T const& myObj;
public:
    HexDump( T const& obj ) : myObj( obj ) {}
    friend std::ostream& operator<<( std::ostream& dest, HexDump const& obj )
    {
        dest.fill( '0' );
        dest.setf( std::ios_base::hex, std::ios_base::basefield );
        uint32_t const* p = reinterpret_cast<uint32_t const*>( &obj.myObj );
        for ( int i = 0; i < sizeof(T) / sizeof(uint32_t); ++ i ) {
            if ( i != 0 ) {
                dest << ' ';
            }
            dest << std::setw( sizeof(uint32_t) * 2 ) << p[i];
        }
        return dest;
    }
};

template <typename T>
HexDump<T>
hexDump( T const& obj )
{
    return HexDump<T>( obj );
}

int
addrDiff( void const* lhs, void const* rhs )
{
    return static_cast<char const*>( lhs ) - static_cast<char const*>( rhs );
}

int
main()
{
    B* aD = new D;
    B* anE = new E;
    std::cout << "position of B in D: " << addrDiff( aD, dynamic_cast<D*>( aD ) ) << std::endl;
    std::cout << "position of B in E: " << addrDiff( anE, dynamic_cast<E*>( anE ) ) << std::endl;

    std::cout << "D: " << hexDump( *dynamic_cast<D*>( aD ) ) << std::endl;
    std::cout << "E: " << hexDump( *dynamic_cast<E*>( anE ) ) << std::endl;
    return 0;
}

The initialization of the various data elements allow you to see the position of the different classes (or their payloads) clearly.

Note in particular that the vptr of the B sub-object is different in a D and in a B; the RTTI infos in the vtbl it points to must reflect the most derived class.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I'm sorry, I don't quite understand what you are saying :) I understand (roughly) how objects are represented in memory for basic inheritance (they just follow their base classes in memory). However, when a dynamic_cast is performed it needs to check whether the types are in fact related? To do this, it accesses the type_info object. So my question was does the type_info object contain all classes that object derives from, or does it only contain the immediate-base classes and then you have to obtain their type_infos to continue up the inheritance hierarchy? – intrigued_66 Jul 30 '14 at 09:44
  • @mezamorphic It needs more than just the info as to whether the classes are related; it needs information as to how to modify the pointer to the object. And this information will be different in each class. So the type info for `D` and `E` must contain the relevant info for `B`; the implementation can't go through `L` or `R` to find it, because the information would be different. – James Kanze Jul 30 '14 at 09:47
  • My question kind of follows on from this one: http://stackoverflow.com/questions/18359780/how-is-dynamic-cast-implemented – intrigued_66 Jul 30 '14 at 09:48
  • James, but the compiler knows which classes are related at compile time. So for a very naïve approach why can't the compiler just list all the base classes, in each type_info object, for each polymorphic type? In my simplistic example the type_info of D would say {L, R, B}. I understand you say it would need to do more work to do the actual cast, but surely ascertaining whether it can do the cast just requires knowing the base classes? I must be missing something as this doesn't seem difficult? – intrigued_66 Jul 30 '14 at 09:51
  • To further my example, if we had "class A", "class B : public A" and "class C : public B" we know the type info for B would be {A,B} (assuming B can dynamic_cast itself?), and type_info for C would be {A, B, C}. I do not know as much about multiple or virtual inheritance, so perhaps this is why I am not fully understanding you. – intrigued_66 Jul 30 '14 at 10:00
  • @mezamorphic The only time you need to determine whether you can do the actual cast is when you are trying to do the cast. The information in the base classes does not allow you to do the cast, so it must be present in the most derived class. Some sort of dynamic look up is used to find the information, in the type infos of the derived class; if this fails, then you can't do the cast; if it succeeds, you use the information to do the cast. – James Kanze Jul 30 '14 at 10:39
  • @mezamorphic For simple, direct inheritance, there will normally be only one vtable (once the object is fully constructed); the vtable for the most derived class (which will subsume the vtables for the base classes). Once multiple inheritance comes into play, it becomes more complicated, _but_ the type info is always that of the most derived class, which contains the various informations necessary for the conversion of pointers in `dynamic_cast`. – James Kanze Jul 30 '14 at 10:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/58340/discussion-between-mezamorphic-and-james-kanze). – intrigued_66 Jul 30 '14 at 10:52
  • I think you are misinterpreting my question (possibly). I know the check needs to be done at run-time. My question is whether the compiler can populate an object's type_info object with a list of ALL the classes which that object may legally be dynamically casted to, so that only one type info object needs to be checked. If this wasn't the case, would the run-time system have to obtain a type_info object for one class and use that to obtain the type_info object for another class, etc until it has determined whether the cast can be performed (not HOW it is performed, just IF). – intrigued_66 Jul 30 '14 at 11:42
  • I am just trying to ask if the type_info object contains exhaustive information for whether the object can be casted (and therefore no other type_info objects need to be checked), or whether type_info objects for each class in the hierarchy need to be checked recursively? – intrigued_66 Jul 30 '14 at 11:44
  • I think I addressed that. Not only can the compiler populate an object's type info with _all_ of the classes involved in the inheritance hierarchy, it _must_. No other implementation works. – James Kanze Jul 30 '14 at 12:18
  • So only one vtable look-up and type_info retrieval is done during dynamic_cast and this is for the object passed in to the cast? – intrigued_66 Jul 30 '14 at 12:39
  • @mezamorphic The type info that the code gets is the one which corresponds to the most derived class pointed to. No others are necessary. – James Kanze Jul 30 '14 at 13:23