spolsky-joel@CS.YALE.EDU (Joel Spolsky) (12/18/90)
Hi, Perhaps some kind soul can tell me why this program fragment doesn't work? I am trying to use a base pointer and get the correct derived method, but this doesn't work when the base pointer has been cast to a void * and then back to the base pointer. [Why, you may ask, do I need to cast this through a void pointer? well, because I am using InterViews, and I am using InterViews "Pictures" to store things, and when I get them back they have been cast into Graphic *'s, so I have to cast them back, but by then the derived methods don't work. Arg!] (I'm using g++ version 1.37.1 for Sun SPARC station, Sun OS 4.0) #include "std.h" class a {int x;}; class b {float x,y;}; class Tiger { public: virtual void Roar (void) {printf("roar\n");} }; class Bengal: public a, public Tiger{ public: virtual void Roar (void) {printf("bengal roar\n");} }; class Senegal: public b, public Tiger{ public: virtual void Roar (void) {printf("senegal roar\n");} }; main() { Senegal *ps = new Senegal; // This works, printing "senegal roar": printf("cast to Tiger *:\n"); ((Tiger *) ps)->Roar(); // This segment faults: printf("cast thru:\n"); ((Tiger *) (void *) ps)->Roar(); } Note: There is no problem if Bengal and Senegal do _not_ multiple inherit. Thanks in advance for your help, -- Joel Spolsky // And these streets, Quiet as a sleeping army spolsky@cs.yale.edu // Send their battered dreams to heaven. _Paul Simon
kaiser@ananke.stgt.sub.org (Andreas Kaiser) (12/19/90)
In a message of <Dec 18 06:50>, Joel Spolsky (spolsky-joel@CS.YALE.EDU ) writes:
JS> class Tiger {
JS> public:
JS> virtual void Roar (void)
JS> {printf("roar\n");}
JS> };
JS> class Senegal: public b, public Tiger{
JS> public:
JS> virtual void Roar (void)
JS> {printf("senegal roar\n");}
JS> };
Consider the implementation of Senegal: Since it has two base classes, at least
one of those has a non-zero offset within the derived class. Since typically the
first has zero offset (but don't rely on this fact, sinceimplementations may
vary), the base class Tiger has not.
JS> main()
JS> {
JS> Senegal *ps = new Senegal;
JS> // This works, printing "senegal roar":
JS> printf("cast to Tiger *:\n");
JS> ((Tiger *) ps)->Roar();
The compiler recognizes that Tiger is a base class of your object of type
Senegal and converts the pointer by adding the offset mentioned above.
JS> // This segment faults:
JS> printf("cast thru:\n");
JS> ((Tiger *) (void *) ps)->Roar();
But now the compiler does not know anything aboid the pointer, since it is a
void pointer. So the cast is in effect s noop and does not add or subtract a
constant.
Try
printf ("%p %p\n", (Tiger *) ps, (Tiger *) (void *) ps);
and you'll see the difference.
In the case of single inheritance, the offset typically is zero, so both are the
same and program works.
But you hit a point. This is a real problem in C++. Until the day when templates
are implemented in most available compilers, void pointers have to be used in
some circumstances. Maybe this problem is the reason, why Bjarne Stroustup left
the implicit conversion TO (void *), but removed the implicit conversion FROM
(void *) from the language. Type casts are not necessarily transitive
operations.
Hint: replace
(Tiger *) (void *) ps
by
(Tiger *) (void *) (Tiger *) ps
- If you have a program, where a (void *) is converted to (A *) before use,
always carry out the conversion to (A *) BEFORE converting to (void *).
- Always make sure, that the conversion to and from some pointer type converts
between exactly the same types.
- Do not allow a cast to and from some type to carry out an implicit third
conversion (in your example: (Tiger *) ps).
Andreas
mat@mole-end.UUCP (Mark A Terribile) (12/21/90)
> Hi, Perhaps some kind soul can tell me why this program fragment > doesn't work? I am trying to use a base pointer and get the correct > derived method, but this doesn't work when the base pointer has been > cast to a void * and then back to the base pointer. > Note: There is no problem if Bengal and Senegal do _not_ multiple > inherit. Given base class B and derived class D, these two code fragments are NOT equivalent. B* bp = ... D* dp = (D*)bp; B* bp = ... D* dp = (D*)(void*)bp; They are NOT equivalent. They are NOT equivalent. They are NOT equivalent. Nor are they equivalent. Why not? Because if B is a base class (direct or indirect) of D, the conversion between B* and D* may involve adding or subracting an offset. Conversion to and from void*, on the other hand, preserves the bit pattern (does not add or subtract any offset ...) There's a good deal of magic going on behind the scenes here (and in other places as well). You can play games, but there are certain rules you must obey. One is that if you have to store a pointer as a void*, you must first retrieve it as it was stored. This may mean that you have to store it according to how you mean to retrieve it. If it will be retrieved as a pointer-to-base, you must first cast it to pointer-to-base and THEN to void* . -- (This man's opinions are his own.) From mole-end Mark Terribile