|
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