Topic: Where does this break?


Author: klamer@mi.el.utwente.nl (Klamer Schutte)
Date: Mon, 1 Mar 1993 10:25:25 GMT
Raw View
Hi,

I consider using a piece of code which basically does this:

class A
{
public:
 // Simple data (integers) and members.
 // This is automatically generated code, and i don't want to change it.
 // No constructor / desctructor

 void init_me(void **ptr)
  { *ptr = this; }
};

class B : public A
{
 C object; // C fancy class designed by myself

public:
 B(void **ptr)
  { init_me( ptr ); }
 // some members
};

void *the_ptr;

...

B *b = new B( &the_ptr ); // So basically, the_ptr = (A *)&b;

...

B *b_ptr = (B *) the_ptr; // So b_ptr = (B *)(void *)(A *)&b;

And the rest of the code assumes that b_ptr == b. I am using g++ 2.3.3 now,
and this seems to work. I also know that it is not allowed according to ARM.
And since fixing the (automatic generated) code is a lot of work, i have the
next question:

Does anybody know of an exsisting C++ compiler where the above construct does
not work? Does anybody think future compilers will not work with the above
code?

And the other question is:

What changes in the above code do break it, on compilers which currently
work with the above code? (Yes, i have thought of changing A into
a virtual base class of B. So i don't :-)

Perhaps it also might be interesting to know:
1) whether a lot of people use kludges as above
2) whether people really think that code such as above should be avoided on
   all occasions.

I will summarize responses i get by e-mail.

Klamer

--
Klamer Schutte   Tel: +31-53-892778 Fax: +31-53-340045
Faculty of electrical engineering -- University of Twente, The Netherlands
preferred: klamer@mi.el.utwente.nl     SMTP: klamer@utelmi01.el.utwente.nl




Author: steve@taumet.com (Steve Clamage)
Date: Wed, 3 Mar 1993 17:21:05 GMT
Raw View
klamer@mi.el.utwente.nl (Klamer Schutte) writes:

>class A
>{
>public:
> void init_me(void **ptr) { *ptr = this; }
>};

>class B : public A
>{
>public:
> B(void **ptr) { init_me( ptr ); }
>};

>void *the_ptr;

>B *b = new B( &the_ptr ); // So basically, the_ptr = (A *)&b;
>B *b_ptr = (B *) the_ptr; // So b_ptr = (B *)(void *)(A *)&b;


No, this does not in general work as you want.  When it does, it is
only by accident.

Conversion of pointers within a class hierarchy in general requires
execution of code.  The compiler must rely on the static (declared)
types of objects to perform casts.  Once you cast to a void*, you
throw away all the type information.  Any needed adjustments can no
longer occur.  In the last line, any adjustment required to go from
A* to B* would not occur.

Most (all?) compilers lay out classes so that a derived class with
one immediate non-virtual base class begins at the same address as
the base class, and no pointer conversion is needed.  This layout
is not a language requirement, so you should not depend on it.

Even if the layout were required, the practice is a bug waiting
to happen.  Seemingly innocent changes to the hierarchy could
result in program failure with no warning from the compiler.
--

Steve Clamage, TauMetric Corp, steve@taumet.com