Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > dynamic_cast from base to another parent of derived class

Reply
Thread Tools

dynamic_cast from base to another parent of derived class

 
 
Boris
Guest
Posts: n/a
 
      10-03-2006
I'm porting code from Windows to UNIX and ran into a problem with
dynamic_cast. Imagine a class hierarchy with three levels: class Level2
derives from Level1 which derives from Base. If you look now at this code:

Base *b = new Level2();
Level1 *l1 = dynamic_cast<Level1*>(b);

Should dynamic_cast return a valid pointer or 0? I wonder as Visual Studio
2005 returns a valid pointer while g++ 3.4.6 returns 0. Both compilers work
as expected when dynamic_cast<Level2*> is used but return different results
with the code above. Who is right?

The class hierarchy I'm talking about has some more classes per level.
That's why sometimes a dynamic_cast to a level 1 class is preferred as you
can then operate on various level 2 classes which are all derived from the
same level 1 class. If you have to downcast to the actual level 2 class you
end up writing the same code for several level 2 classes which I would like
to avoid.

Boris


 
Reply With Quote
 
 
 
 
Pete Becker
Guest
Posts: n/a
 
      10-03-2006
Boris wrote:
> I'm porting code from Windows to UNIX and ran into a problem with
> dynamic_cast. Imagine a class hierarchy with three levels: class Level2
> derives from Level1 which derives from Base. If you look now at this code:
>
> Base *b = new Level2();
> Level1 *l1 = dynamic_cast<Level1*>(b);
>
> Should dynamic_cast return a valid pointer or 0? I wonder as Visual Studio
> 2005 returns a valid pointer while g++ 3.4.6 returns 0. Both compilers work
> as expected when dynamic_cast<Level2*> is used but return different results
> with the code above. Who is right?
>


If Base has at least one virtual function (a prerequisite for applying
dynamic_cast to a Base*) and that the various bases are public,
dynamic_cast<Level1*>(b) does just what you would expect: it returns a
pointer to the Level1 subobject in the original Level2 object.

--

-- Pete

Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." For more information about this book, see
www.petebecker.com/tr1book.
 
Reply With Quote
 
 
 
 
Boris
Guest
Posts: n/a
 
      10-03-2006
Pete Becker wrote:
> Boris wrote:
>> I'm porting code from Windows to UNIX and ran into a problem with
>> dynamic_cast. Imagine a class hierarchy with three levels: class
>> Level2 derives from Level1 which derives from Base. If you look now
>> at this code: Base *b = new Level2();
>> Level1 *l1 = dynamic_cast<Level1*>(b);
>>
>> Should dynamic_cast return a valid pointer or 0? I wonder as Visual
>> Studio 2005 returns a valid pointer while g++ 3.4.6 returns 0. Both
>> compilers work as expected when dynamic_cast<Level2*> is used but
>> return different results with the code above. Who is right?
>>

>
> If Base has at least one virtual function (a prerequisite for applying
> dynamic_cast to a Base*) and that the various bases are public,
> dynamic_cast<Level1*>(b) does just what you would expect: it returns a
> pointer to the Level1 subobject in the original Level2 object.


Thanks for your reply, Pete! When I create a small test program which works
with the original class hierarchy there is no problem with dynamic_cast.

The code I talk about looks like this:

if (typeid(b) == typeid(level2))
{
const level1 *l1 = dynamic_cast<const level1*>(&b);
}

There is an explicit check (b is a reference to an instance of Base here)
before dynamic_cast is used. The requirements you mentioned above are also
met (Base has virtual functions and public inheritance is used everwhere).
The compiler doesn't complain either when it sees this code. When I
dynamic_cast to level2* I get a valid pointer, too. I play around some more
to figure out what's going on. If you or anyone else has an idea what might
affect dynamic_cast I would be happy to hear it!

Boris


 
Reply With Quote
 
mlimber
Guest
Posts: n/a
 
      10-03-2006
Boris wrote:
> Pete Becker wrote:
> > Boris wrote:
> >> I'm porting code from Windows to UNIX and ran into a problem with
> >> dynamic_cast. Imagine a class hierarchy with three levels: class
> >> Level2 derives from Level1 which derives from Base. If you look now
> >> at this code: Base *b = new Level2();
> >> Level1 *l1 = dynamic_cast<Level1*>(b);
> >>
> >> Should dynamic_cast return a valid pointer or 0? I wonder as Visual
> >> Studio 2005 returns a valid pointer while g++ 3.4.6 returns 0. Both
> >> compilers work as expected when dynamic_cast<Level2*> is used but
> >> return different results with the code above. Who is right?
> >>

> >
> > If Base has at least one virtual function (a prerequisite for applying
> > dynamic_cast to a Base*) and that the various bases are public,
> > dynamic_cast<Level1*>(b) does just what you would expect: it returns a
> > pointer to the Level1 subobject in the original Level2 object.

>
> Thanks for your reply, Pete! When I create a small test program which works
> with the original class hierarchy there is no problem with dynamic_cast.
>
> The code I talk about looks like this:
>
> if (typeid(b) == typeid(level2))
> {
> const level1 *l1 = dynamic_cast<const level1*>(&b);
> }
>
> There is an explicit check (b is a reference to an instance of Base here)
> before dynamic_cast is used. The requirements you mentioned above are also
> met (Base has virtual functions and public inheritance is used everwhere).
> The compiler doesn't complain either when it sees this code. When I
> dynamic_cast to level2* I get a valid pointer, too. I play around some more
> to figure out what's going on. If you or anyone else has an idea what might
> affect dynamic_cast I would be happy to hear it!
>
> Boris


One syntactic simplification:

if ( const level1 *const l1 = dynamic_cast<const level1*>(&b) )
{
// Use l1 here
}
else
{
// l1 is null here
}

Cheers! --M

 
Reply With Quote
 
Boris
Guest
Posts: n/a
 
      10-03-2006
Boris wrote:
> [...] The code I talk about looks like this:
>
> if (typeid(b) == typeid(level2))
> {
> const level1 *l1 = dynamic_cast<const level1*>(&b);
> }


I changed the code for testing purposes and replaced dynamic_cast with
reinterpret_cast - this works.

I changed then the code to make it look like this:

if (typeid(b) == typeid(level2))
{
const level2 *l2 = dynamic_cast<const level2*>(&b);
const level1 *l1 = l2;
}

This works, too. Is there any other possible explanation than a compiler bug
why a dynamic_cast<const level1*> should not work?

Boris


 
Reply With Quote
 
Boris
Guest
Posts: n/a
 
      10-03-2006
[crossposted to gnu.g++.help]

Boris wrote:
> Boris wrote:
>> [...] The code I talk about looks like this:
>>
>> if (typeid(b) == typeid(level2))
>> {
>> const level1 *l1 = dynamic_cast<const level1*>(&b);
>> }

>
> I changed the code for testing purposes and replaced dynamic_cast with
> reinterpret_cast - this works.
>
> I changed then the code to make it look like this:
>
> if (typeid(b) == typeid(level2))
> {
> const level2 *l2 = dynamic_cast<const level2*>(&b);
> const level1 *l1 = l2;
> }
>
> This works, too. Is there any other possible explanation than a
> compiler bug why a dynamic_cast<const level1*> should not work?


It looks like a problem with g++. The code I was talking about is in a
shared library. When I link the executable statically dynamic_cast works.
When I use however the shared library dynamic_cast returns 0. Same code but
different behavior due to linking.

There is a section "dynamic_cast, throw, typeid don't work with shared
libraries" at http://gcc.gnu.org/faq.html#dso. From what I understand though
there is not much you can do? I'm using version 3.4.6 of g++ which is the
latest of the 3.4.x series.

The code construct as above with dynamic_cast is used in various project
files. When building and linking the shared library some work, others don't.
I don't know though on what it depends why some dynamic_casts don't work and
falsely return 0. Is there anything I can do except replacing all
dynamic_casts? Any g++ experts with ideas?

Boris


 
Reply With Quote
 
Maxim Yegorushkin
Guest
Posts: n/a
 
      10-04-2006

Boris wrote:
> [crossposted to gnu.g++.help]
>
> Boris wrote:
> > Boris wrote:
> >> [...] The code I talk about looks like this:
> >>
> >> if (typeid(b) == typeid(level2))
> >> {
> >> const level1 *l1 = dynamic_cast<const level1*>(&b);
> >> }

> >
> > I changed the code for testing purposes and replaced dynamic_cast with
> > reinterpret_cast - this works.
> >
> > I changed then the code to make it look like this:
> >
> > if (typeid(b) == typeid(level2))
> > {
> > const level2 *l2 = dynamic_cast<const level2*>(&b);
> > const level1 *l1 = l2;
> > }
> >
> > This works, too. Is there any other possible explanation than a
> > compiler bug why a dynamic_cast<const level1*> should not work?


Why not just:

if(level2 const* l2 = dynamic_cast<level2 const*>(&b))
{
// no need comparing typeinfo's
}

?

>
> It looks like a problem with g++. The code I was talking about is in a
> shared library. When I link the executable statically dynamic_cast works.
> When I use however the shared library dynamic_cast returns 0. Same code but
> different behavior due to linking.
>
> There is a section "dynamic_cast, throw, typeid don't work with shared
> libraries" at http://gcc.gnu.org/faq.html#dso.


I wonder how you reached such a conclusion. Where on that page do they
say it does not work?

 
Reply With Quote
 
Boris
Guest
Posts: n/a
 
      10-04-2006
Maxim Yegorushkin wrote:
> [...]
>
> Why not just:
>
> if(level2 const* l2 = dynamic_cast<level2 const*>(&b))
> {
> // no need comparing typeinfo's
> }
>
> ?


I want to cast to a level 1 class which is a parent of several level 2
classes. Otherwise you are right - I could cast to the actual type directly.
The real code looks more like this:

if (typeid(b) == typeid(level2a) || typeid(b) == typeid(level2b) ||
typeid(b) == typeid(level2c))
{
const level1 *l1 = dynamic_cast<const level1*>(&b);
}

Class level1 is the parent of all the level2 classes. I have to check for
several types but don't need to write the same code then at least for all
the level2 classes.

>> It looks like a problem with g++. The code I was talking about is in
>> a shared library. When I link the executable statically dynamic_cast
>> works. When I use however the shared library dynamic_cast returns 0.
>> Same code but different behavior due to linking.
>>
>> There is a section "dynamic_cast, throw, typeid don't work with
>> shared libraries" at http://gcc.gnu.org/faq.html#dso.

>
> I wonder how you reached such a conclusion. Where on that page do they
> say it does not work?


The title is pretty clear I think: "dynamic_cast,throw, typeid don't work
with shared libraries". They are talking about template instantiations,
vague linkage, not an exhaustive list, namesspaces etc. This all doesn't
sound like adding a linker switch fixes all your problems. This sounds like
be ready for all kind of problems.

Anyway if I can downcast to the actual type and then back to its parent with
the following code (no compilation error, no runtime error) why should a
dynamic_cast to the parent l1 not work?

if (typeid(b) == typeid(level2))
{
const level2 *l2 = dynamic_cast<const level2*>(&b);
const level1 *l1 = l2;
}

Boris


 
Reply With Quote
 
Boris
Guest
Posts: n/a
 
      10-04-2006
Boris wrote:
>> I wonder how you reached such a conclusion. Where on that page do
>> they say it does not work?

>
> The title is pretty clear I think: "dynamic_cast,throw, typeid don't
> work with shared libraries". They are talking about template
> instantiations, vague linkage, not an exhaustive list, namesspaces
> etc. This all doesn't sound like adding a linker switch fixes all
> your problems. This sounds like be ready for all kind of problems.
>
> Anyway if I can downcast to the actual type and then back to its
> parent with the following code (no compilation error, no runtime
> error) why should a dynamic_cast to the parent l1 not work?
>
> if (typeid(b) == typeid(level2))
> {
> const level2 *l2 = dynamic_cast<const level2*>(&b);
> const level1 *l1 = l2;
> }


I changed the code to use boost:olymorphic_downcast instead of
dynamic_cast:

if (typeid(b) == typeid(level2))
{
const level1 *l1 = boost:olymorphic_downcast<const level1*>(&b);
}

With NDEBUG defined boost:olymorphic_downcast uses only static_cast - this
works. Without NDEBUG defined dynamic_cast is used which doesn't work (as an
assert fails).

Summary: Everything works with reinterpret_cast and static_cast or
dynamic_cast when statically linked. It fails when dynamic_cast is used in a
shared library. It can not be reproduced with a small test case as
everything works then. The project I port to UNIX uses dynamic_casts in
different files where some do work. There must be some more conditions which
make g++ produce wrong code and which make some dynamic_casts fail
constantly.

Boris


 
Reply With Quote
 
Maxim Yegorushkin
Guest
Posts: n/a
 
      10-04-2006

Boris wrote:
> Maxim Yegorushkin wrote:
> > [...]
> >
> > Why not just:
> >
> > if(level2 const* l2 = dynamic_cast<level2 const*>(&b))
> > {
> > // no need comparing typeinfo's
> > }
> >
> > ?

>
> I want to cast to a level 1 class which is a parent of several level 2
> classes. Otherwise you are right - I could cast to the actual type directly.
> The real code looks more like this:
>
> if (typeid(b) == typeid(level2a) || typeid(b) == typeid(level2b) ||
> typeid(b) == typeid(level2c))
> {
> const level1 *l1 = dynamic_cast<const level1*>(&b);
> }
>
> Class level1 is the parent of all the level2 classes. I have to check for
> several types but don't need to write the same code then at least for all
> the level2 classes.


I would redesign my hierarchy so, that dynamic_cast would be enough.

> >> It looks like a problem with g++. The code I was talking about is in
> >> a shared library. When I link the executable statically dynamic_cast
> >> works. When I use however the shared library dynamic_cast returns 0.
> >> Same code but different behavior due to linking.
> >>
> >> There is a section "dynamic_cast, throw, typeid don't work with
> >> shared libraries" at http://gcc.gnu.org/faq.html#dso.

> >
> > I wonder how you reached such a conclusion. Where on that page do they
> > say it does not work?

>
> The title is pretty clear I think: "dynamic_cast,throw, typeid don't work
> with shared libraries". They are talking about template instantiations,
> vague linkage, not an exhaustive list, namesspaces etc. This all doesn't
> sound like adding a linker switch fixes all your problems. This sounds like
> be ready for all kind of problems.


This is a title of a frequently asked question. The answer is given fow
to make it work. If you can follow the first advise, which is

<q>
For a program which is linked against a shared library, no additional
precautions are needed
</q>

then no other action is required.

> Anyway if I can downcast to the actual type and then back to its parent with
> the following code (no compilation error, no runtime error) why should a
> dynamic_cast to the parent l1 not work?
>
> if (typeid(b) == typeid(level2))
> {
> const level2 *l2 = dynamic_cast<const level2*>(&b);
> const level1 *l1 = l2;
> }


You could find more help if you posted simplified complete code that
reproduces what you are observing.

It might be that you derive privately from your base (class A : B {}
.. dynamic_cast in this case does not work, reinterpret_cast does work
if your base class is first in the list of the base classes.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
If a class Child inherits from Parent, how to implementChild.some_method if Parent.some_method() returns Parent instance ? metal Python 8 10-30-2009 10:31 AM
Casting from base to derived class in base constructor pastbin@gmail.com C++ 2 02-07-2008 02:41 PM
Derived::Derived(const Base&) and Derived& operator=(const Base&) developereo@hotmail.com C++ 1 05-23-2007 01:44 PM
Derived::Derived(const Base&) developereo@hotmail.com C++ 4 05-23-2007 09:32 AM
Derived::Derived(const Base&) and Derived& operator=(const Base&) developereo@hotmail.com C++ 1 05-23-2007 12:07 AM



Advertisments