Multiple inheritance: unexpected result after transferring from void * to the second base class

My program should use void * to transfer data or objects into a dynamic call situation so that it can refer to data of arbitrary types, even primitive types. However, I recently discovered that the process of dropping these void * in the case of classes with several base classes fails and even my program crashes after calling methods on these hidden pointers, even if the memory addresses seem correct. A crash occurs while accessing "vtable".

So, I created a small test case, the environment is gcc 4.2 on Mac OS X:

class Shape { public: virtual int w() = 0; virtual int h() = 0; }; class Square : public Shape { public: int l; int w() {return l;} int h() {return l;} }; class Decorated { public: int padding; int w() {return 2*padding;} int h() {return 2*padding;} }; class DecoratedSquare : public Square, public Decorated { public: int w() {return Square::w() + Decorated::w();} int h() {return Square::h() + Decorated::h();} }; #include <iostream> template <class T> T shape_cast(void *vp) { // return dynamic_cast<T>(vp); // not possible, no pointer to class type // return static_cast<T>(vp); // return T(vp); // return (T)vp; return reinterpret_cast<T>(vp); } int main(int argc, char *argv[]) { DecoratedSquare *ds = new DecoratedSquare; ds->l = 20; ds->padding = 5; void *dsvp = ds; std::cout << "Decorated (direct)" << ds->w() << "," << ds->h() << std::endl; std::cout << "Shape " << shape_cast<Shape*>(dsvp)->w() << "," << shape_cast<Shape*>(dsvp)->h() << std::endl; std::cout << "Square " << shape_cast<Square*>(dsvp)->w() << "," << shape_cast<Square*>(dsvp)->h() << std::endl; std::cout << "Decorated (per void*) " << shape_cast<Decorated*>(dsvp)->w() << "," << shape_cast<Decorated*>(dsvp)->h() << std::endl; std::cout << "DecoratedSquare " << shape_cast<DecoratedSquare*>(dsvp)->w() << "," << shape_cast<DecoratedSquare*>(dsvp)->h() << std::endl; } 

outputs the following result:

 Decorated (direct)30,30 Shape 30,30 Square 30,30 Decorated (per void*) 73952,73952 DecoratedSquare 30,30 

As you can see, the result "Awarded (for void *)" is completely erroneous. It should also be 30.30, as in the first line.

Whatever casting method I use in shape_cast (), I always get the same unexpected results for the Decorated part. Something is completely wrong with these void *.

From my understanding of C ++, this should be really working. Is there a chance to make this work with void *? Could this be a bug in gcc?

thanks

+7
c ++ gcc casting multiple-inheritance void-pointers
source share
3 answers

This is not a compiler error - this is what reinterpret_cast does. The DecoratedSquare object will be laid out in memory like this:

 Square Decorated DecoratedSquare specific stuff 

Converting a pointer to this into void* will give the address of the beginning of this data without knowing what type it is. reinterpret_cast<Decorated*> will accept this address and interpret everything that is, as Decorated , but Square is the actual memory content. This is wrong, so you get undefined behavior.

You should get the correct results if reinterpret_cast matches the correct dynamic type (i.e. DecoratedSquare ) and then converts to the base class.

+8
source share

Repeat ten times - the only thing you can safely do with the reinterpret_cast pointer is reinterpret_cast to return to the same type of pointer from which it came. The same thing happens with void* conversions: you must convert back to the original type.

So, if you drop DecoratedSquare* to void* , you should return it back to DecoratedSquare* . Not Decorated* , not Square* , not Shape* . Some of them may work on your machine, but this is a combination of luck and behavior specific to a particular implementation. Usually it works with one inheritance, because there is no obvious reason for implementing object pointers in such a way that it does not prevent it from working, but it is not guaranteed and cannot work at all for multiple inheritance.

You say that your code refers to "arbitrary types, including primitive types" via void *. There is nothing wrong with that - apparently, the one who receives the data knows how to treat it as DecoratedSquare* , and not as, say, int* .

If the one who receives it knows only to consider it as a base class, for example Decorated* , then whoever converts it to void* should first have static_cast in the base class, and then in void* :

 void *decorated_vp = static_cast<Decorated*>(ds); 

Now, when you click decorated_vp on Decorated* , you will get the static_cast<Decorated*>(ds) result that you need.

+11
source share

static_cast or dynamic_cast in the presence of multiple inheritance can change the representation of a pointer by shifting it so that it indicates the correct address. static_cast determines the correct offset by looking at the static input information. dynamic_cast does this by checking the dynamic type. If you switch to void *, you will lose all information about the static type and the ability to get information about dynamic typing, so the reinterpret_cast you are using assumes that the offset is null, otherwise several times.

+2
source share

All Articles