Boost logo

Boost :

From: Robert Ramey (ramey_at_[hidden])
Date: 2003-11-18 15:58:52


>Robert Ramey wrote:

>>>I'm just trying to find out why void_cast tests fail on gcc 3.3. Could you
>>>answer two quick questions:
>>
>>>1. Why void_cast is needed in the first place. It's hard to debug it
>>>without fully understanding the purpose.
>>
>> This is vaguely touched upon in the manual. I can be found in the last
>> paragraph of
>> the section "Pointers to Objects of Derived Classes".

>I'd say that this section can confuse the reader. For a user, as opposed to
>developer, it's not obvious why conversion between base and derived classes
>should be done in some special way.

>> For a better
>> illustration of what it does look for void _downcast in oserializer.hpp
>> where pointers of an abstract base type
>> are converted to a pointer of the derived type. typeid gives it the most
>> derived type at
>> run time. Now how do we adjust the pointer from the base type to the
>> correct derived type ?

>IOW, you mean situation like

> D* d = new D;
> B* b = d;
> oa << b;

>?
    
>> void * derived_ptr = static_cast<?>(base_ptr)
>>
>> since ? isn't known until runtime we're stuck.

>But... why do we need to cast void* to base into void* into derived? If I
>understand correctly, in the case above you'll first call register_type,
>which creates basic_serializer*. At this point, you still don't know the
>most derived type. So, supposedly basic_serializer* actually points to
>oserializer_pointer<T>. That instance does real saving of pointer, and the
>first thing it does is:

> T * t = static_cast<T *>(const_cast<void *>(x));

>Why the first cast from base to derived is needed at all? I really can't
>understand this.

Lets expand your example:

Once upon a time in a module far, far away, the following
code is executed.

    D1* d1 = new D1;
    D2* d2 = new D2;
    B* b = d1;
// sometime later in the serialize module the following is invoked
    oa << b;
// at compile time it cannot not be known whether b points
// to a B, the B part of a D1, the B part of a D2. It could be
// any one of the three possibilities.

this invokes the following code from oserializer.hpp. We add some extra comments (////)
to illustrate what happens in a this specific example.

// at compile time T = B
// at runtime t is a reference to either a B or the B part of a D1 or D2.
// at runtimt bos_ptr points to a serializer for a type T poiner
static void save(
Archive &ar,
const T & t,
const basic_oserializer * bos_ptr
){
   //// this returns the extended_type_info record for B
const extended_type_info * this_type
= type_info_implementation<T>::type::get_instance();
   assert(NULL != this_type);
   // retrieve the true type of the object pointed to
   //// this returns the extended_type_info record for D1
   const extended_type_info * true_type
      = type_info_implementation<T>::type::get_derived_extended_type_info(t);
   //// it is the registration system (either by key export or "register")
   //// that guarentees that such a record exists.
   // note:if this assertion is invoked, be sure that derived pointer
   // is either registered or exported.
   assert(NULL != true_type);

   const void *vp = static_cast<const void *>(&t);
   //// at this point vp points the B part of structure D1
   //// (derived from B)

   //// only at runtime can it be determined that the type being
   //// saved (D1) is not the same as T (B). So we compare the two
   //// type information records we just looked up.
   //// if t is actually refers to a T there would be nothing to do
    
   if(*this_type != *true_type){
       //// t(D1) refers to something other than a T (B)
       //// so shift the vp from pointing to B to point to
       //// the D1 part of t.
       // convert pointer to more derived type
       vp = void_downcast(*this_type, *true_type, &t);
       assert(NULL != vp);

       //// set the pointer to the serializer to the serializer
       //// corresponding to a D1
       bos_ptr = basic_oserializer::find(*true_type);
   }
   //// now we know that vp points to a D1
   //// and bos_ptr points to a pointer serializer for a type D1
   assert(NULL != bos_ptr);
   ar.save_pointer(vp, *bos_ptr);
}
 
Which step above is avoidable? How could this be written without void_cast?
We're passed a reference a base class but what we need is a reference to
the most derived class - and that is not available until runtime when its too late
to write a cast. void_cast invokes all the casts at compile time and saves
them for runtime when we can pick the appropriate one.

I hope this helps.

Robert Ramey


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk