dynamic cast and typeid

M

matish

Hi,

should

dynamic_cast<ClassName*>(pointer)

always succeed if

typeid(*pointer).name() == string("ClassName")?

I experienced an unexplainable (to me) behaviour in witch if I call a
virtual function on a pointer of a superclass A the method of the actual
subclass B is (correctly) called, but if I dynamic_cast the pointer to B
the cast returns NULL even if typeid says that the object is of type
B... I'm trying to guess if the one to blame is just myself or I have
any chance to be in front of some kind of compiler bug (witch I consider
quite unlikely)

thx
Matteo
 
A

Alf P. Steinbach

* matish:
Hi,

should

dynamic_cast<ClassName*>(pointer)

always succeed if

typeid(*pointer).name() == string("ClassName")?

No. The result of typeid().name() varies with different compilers.


I experienced an unexplainable (to me) behaviour in witch if I call a
virtual function on a pointer of a superclass A the method of the actual
subclass B is (correctly) called, but if I dynamic_cast the pointer to B
the cast returns NULL even if typeid says that the object is of type
B... I'm trying to guess if the one to blame is just myself or I have
any chance to be in front of some kind of compiler bug (witch I consider
quite unlikely)

See the FAQ about how to post a question about Code That Does Not Work Correctly.

Cheers, & hth.,

- Alf
 
M

matish

Alf P. Steinbach ha scritto:
No. The result of typeid().name() varies with different compilers.

See the FAQ about how to post a question about Code That Does Not Work
Correctly.

Unfortunately writing here a small portion of compilable code that shows
the problem is quite hard. It requires different shared objects and
template function. If the answer to my previous question ware "yes" I
could just drop further explanations. Please forgive me if I just try to
explain it in natural language.

The problem involves two shared objects compiled by the same compiler.
typeid returns the same name for the object I want to cast and the class
I want to cast it into but the dynamic_cast fails. Is it possible even
if everything is compiled by the same compiler?

thx
 
J

Jim Langston

matish said:
Alf P. Steinbach ha scritto:


Unfortunately writing here a small portion of compilable code that
shows the problem is quite hard. It requires different shared objects
and template function. If the answer to my previous question ware
"yes" I could just drop further explanations. Please forgive me if I
just try to explain it in natural language.

The problem involves two shared objects compiled by the same compiler.
typeid returns the same name for the object I want to cast and the
class I want to cast it into but the dynamic_cast fails. Is it
possible even if everything is compiled by the same compiler?

Unfortunately, without code we can only make guesses and usually in the long
run it turns out that a poster means something different than everything
thinks and people wind up wasting everyone's time talking about things that
aren't related.

I know it can be difficult to reduce a problem to a minimally compiled
program that reproduces the error, but a lot of times for someone to attempt
to do this they find the problem themselves. And if not, when the code is
shown everything can test it, read it, etc.. without having to guess what
the poster is actually talking about.

Please give it a try.
 
A

Alf P. Steinbach

* matish:
Alf P. Steinbach ha scritto:


Unfortunately writing here a small portion of compilable code that shows
the problem is quite hard.

Well, I can assure you that telepathy is even harder.

It requires different shared objects and
template function. If the answer to my previous question ware "yes" I
could just drop further explanations. Please forgive me if I just try to
explain it in natural language.

If the reason for using "natural language" is that it allows you to skip over
critical details and be vague instead of technically accurate, consider whether
that's really such a good idea in this context.

The problem involves two shared objects compiled by the same compiler.
typeid returns the same name for the object I want to cast and the class
I want to cast it into but the dynamic_cast fails. Is it possible even
if everything is compiled by the same compiler?

Try to post some code. Code that can be compiled. Just making that example may
help you discover what the problem is.


Cheers, & hth.

- Alf
 
M

matish

Alf P. Steinbach ha scritto:
Try to post some code.

ok, I try to be as concise as I can.

//this one is linked in a.so file:

//file objects.h

#include <string>

class A {
public:
virtual std::string getClassName() const;
}

virtual ~A();

};

class B : public A {};

//file objects.cpp


std::string A::getClassName() const{
return typeid(*this).name();
}

A::~A() {}



//this one is linked in b.so file

//file container.h

#include <iostream>
#include "objects.h"

class Container {
public:
Container();

void setObject(A* o);

template<typename T>
T *getObjectAs() {
T* t = dynamic_cast<T*>(var);

if(t == NULL)
std::cout << "Object is of type " << var->getClassName() <<
" but you required " << typeid(T).name() << std::endl;

return t;
}


private:
A* var;

};

//container.cpp

void Container::setObject(A* o) {
var = o;
}



//now main.c

#include "container.h"
#include "objects.h"

int main() {

B* b = new B();
Container cont;

cont.setObject(b);
B* b1 = cont.getObjectAs<B>();

return 0;
}



Well, The correponding method in my real application prints out:
Object is of type B but you required B

hope this helps in understanding what the code I have does.

ty again.
 
A

Alf P. Steinbach

* matish:
Alf P. Steinbach ha scritto:


ok, I try to be as concise as I can.

//this one is linked in a.so file:

//file objects.h

#include <string>

class A {
public:
virtual std::string getClassName() const;
}

virtual ~A();

};

class B : public A {};

//file objects.cpp


std::string A::getClassName() const{
return typeid(*this).name();
}

A::~A() {}



//this one is linked in b.so file

//file container.h

#include <iostream>
#include "objects.h"

class Container {
public:
Container();

void setObject(A* o);

template<typename T>
T *getObjectAs() {
T* t = dynamic_cast<T*>(var);

if(t == NULL)
std::cout << "Object is of type " << var->getClassName() <<
" but you required " << typeid(T).name() << std::endl;

return t;
}


private:
A* var;

};

//container.cpp

void Container::setObject(A* o) {
var = o;
}



//now main.c

#include "container.h"
#include "objects.h"

int main() {

B* b = new B();
Container cont;

cont.setObject(b);
B* b1 = cont.getObjectAs<B>();

return 0;
}



Well, The correponding method in my real application prints out:
Object is of type B but you required B

hope this helps in understanding what the code I have does.

There are some issues as C++ code: need to include <typeinfo> (I think, at least
g++ says so), & need body for Container default constructor.

However, I think the problem you experience has only to do with use of
dynamically linked libraries.

The C++ standard doesn't address dynamically linked libraries, but from a
practical point of view what's happening is probably that the B-object is set up
(by code in one library) with a vtable that's different from the one
dynamic_cast is checking for (code in another library).

Apart from fixing the design (consider visitor pattern or simply introducing
relevant virtual member functions so you don't have to downcast), you may get
this to "work" by delegating the downcasting to the class in question, like

class B : public A
{
public:
static B* downcasted( A* p );
};


//file objects.cpp

B* B::downcasted( A* p ) { return dynamic_cast<B*>( p ); }


//file container.h

class Container {
public:
Container() {}; // Added body.

void setObject(A* o);

template<typename T>
T *getObjectAs() {
T* t = T::downcasted( var );

if(t == NULL)
std::cout << "Object is of type " << var->getClassName() <<
" but you required " << typeid(T).name() << std::endl;

return t;
}


private:
A* var;

};


Cheers, & hth.,

- Alf
 
M

matish

Alf P. Steinbach ha scritto:


Apart from fixing the design (consider visitor pattern or simply
introducing relevant virtual member functions so you don't have to
downcast), you may get this to "work" by delegating the downcasting to
the class in question

thank you very much. Something, like the container class, is part of a
framework that I cannot easily change...

How bad is a static cast like in:

template<typename T>
T *getObjectAs() {

T *t = dynamic_cast<T*>(obj);
if(t == NULL && obj->getClassName() == typeid(T).name())
t = static_cast<T*>(obj);

return t;

}


?

Is that dangerous only if different shared objects are compiled by
different compilers?

thank you
 
A

Alf P. Steinbach

* matish:
Alf P. Steinbach ha scritto:




thank you very much. Something, like the container class, is part of a
framework that I cannot easily change...

How bad is a static cast like in:

template<typename T>
T *getObjectAs() {

T *t = dynamic_cast<T*>(obj);
if(t == NULL && obj->getClassName() == typeid(T).name())
t = static_cast<T*>(obj);

return t;

}

?

It's /never/ a good idea to use a cast to force the compiler or runtime library
to shut up. You use a cast to say "Shut up, I know what I'm doing". Here you
don't know (at least, not yet) what you're doing, so forcing things via a cast
is the entirely Wrong Thing To Do (WTTD(TM)).

Is that dangerous only if different shared objects are compiled by
different compilers?

Dangerous anyway.

Sheers, & hth.,

- Alf
 
P

Pavel

matish said:
Alf P. Steinbach ha scritto:


Unfortunately writing here a small portion of compilable code that shows
the problem is quite hard. It requires different shared objects and
template function. If the answer to my previous question ware "yes" I
could just drop further explanations. Please forgive me if I just try to
explain it in natural language.

The problem involves two shared objects compiled by the same compiler.
typeid returns the same name for the object I want to cast and the class
I want to cast it into but the dynamic_cast fails. Is it possible even
if everything is compiled by the same compiler?

thx
It's a system-dependent issue. I was hit by it many times before on many
platforms and most of the times it was cheaper to work it around than to
actually solve. I would post it to the OS-specific group. But, in my
practice I rarely could make C++ classes of same hierarchy but loaded
dynamically from different libraries cast-compatible. When I was
successful, it usually included some tweaking with the system-dependent
dynamic linker options, compiler switches and what-not and was quite
fragile, by that I mean:

1. It got broken after seemingly innocent changes in the compilation of
runtime environment so I was not comfortable to deliver the code to the
client. You know, those clients tend to break everything that can and
cannot be broken.

2. Otherwise solid compiler/OS vendors have never been very helpful and
made contradicting statements every other day.

So I suggest that you think of a workaround before delving into this
problem and trying to find a "real" solution.

-Pavel

PS. One of the workarounds I used was switching a project to Java,
another -- switching a part of another project to C. Ironic.. :)
 
P

Pavel

Pavel said:
It's a system-dependent issue. I was hit by it many times before on many
platforms and most of the times it was cheaper to work it around than to
actually solve. I would post it to the OS-specific group. But, in my
practice I rarely could make C++ classes of same hierarchy but loaded
dynamically from different libraries cast-compatible. When I was
successful, it usually included some tweaking with the system-dependent
dynamic linker options, compiler switches and what-not and was quite
fragile, by that I mean:

1. It got broken after seemingly innocent changes in the compilation of meant "or"
runtime environment so I was not comfortable to deliver the code to the
client. You know, those clients tend to break everything that can and
cannot be broken.

2. Otherwise solid compiler/OS vendors have never been very helpful and
made contradicting statements every other day.

So I suggest that you think of a workaround before delving into this
problem and trying to find a "real" solution.

-Pavel

PS. One of the workarounds I used was switching a project to Java,
another -- switching a part of another project to C. Ironic.. :)
 
P

Pavel

matish said:
Alf P. Steinbach ha scritto:




thank you very much. Something, like the container class, is part of a
framework that I cannot easily change...
Then you are in trouble. You need to raise it to the framework's vendor
support and be prepared to involve the OS and Compiler vendors and get
them on the same call. I can only wish you Good Luck.
How bad is a static cast like in:

template<typename T>
T *getObjectAs() {

T *t = dynamic_cast<T*>(obj);
if(t == NULL && obj->getClassName() == typeid(T).name())
t = static_cast<T*>(obj);

return t;

}


?

Is that dangerous only if different shared objects are compiled by
different compilers?
I second Alf -- it is dangerous. If the compilers are different, dynamic
linking will most probably refuse to work outright. But if the compilers
are same (including versions and used compilation options and the
environments) this might initially work and then stop working as your
user switches libraries in his library path (or whatever your
environment for that). And maybe it won't even the user but some
installation wizard of a 3rd-party software.

To summarize: even if this issue is painful, its solution may hurt even
more. Maybe you can revisit dynamic-linking approach. For example, can
you merge Container and Object libraries into one? Remember, it will
always be risky to deploy them separately anyway, because a new version
may use newer compiler or different environment or compiler options or
similar.

-Pavel
 
M

matish

Pavel ha scritto:
It's a system-dependent issue. I was hit by it many times before on many
platforms and most of the times it was cheaper to work it around than to
actually solve.

Should I consider it a compiler issue (that may be fixed in some future)
or something people must know when they learn C++("don't dynamic_cast
across different libraries")?
 
J

James Kanze

* matish:
There are some issues as C++ code: need to include <typeinfo>
(I think, at least g++ says so), & need body for Container
default constructor.

Plus a couple of typos in the class definitions.
However, I think the problem you experience has only to do
with use of dynamically linked libraries.

Or the relationship between templates and dynamic linking.
However...
The C++ standard doesn't address dynamically linked libraries,
but from a practical point of view what's happening is
probably that the B-object is set up (by code in one library)
with a vtable that's different from the one dynamic_cast is
checking for (code in another library).

I'm supposing a Unix platform (given the .so extension)---given
the way Unix does dynamic linking by default, there shouldn't be
any problems, and some quick experiments here, both under Linux
and Solaris (the latter with both g++ and Sun CC) showed no
problems. Two things he should check:

-- Exactly how the dynamic linking is being done. There could
be problems if he's using something other than the defaults
in dlopen, and doing something wrong. (In that case, he
should probably ask in a newsgroup dedicated to his system
and compiler).

-- Make sure everything has been compiled with the same version
of the headers. All too often, this sort of problem occurs
because the dynamically linked object was compiled with an
older or newer version of the class definition. (In other
words, there is a violation of the one definition rule.)

Note that this will also cause problems if the code is
statically linked. The symptoms may be different however,
but above all, since everything is linked at one time, a
good makefile will ensure that everything that is needed
gets recompiled---with dynamic linking, of course, there is
no guarantee at runtime that the library was generated using
the same sources as the main.
 
G

gpderetta

Pavel ha scritto:


Should I consider it a compiler issue (that may be fixed in some future)
or something people must know when they learn C++("don't dynamic_cast
across different libraries")?

If you are using GCC (or any compatible compilers) and dlopening
shared objects, make sure to read this gcc faq item:

http://gcc.gnu.org/faq.html#dso

Opening a shared object by passing RTDL_GLOBAL (instead of the default
RTDL_LOCAL) usually fixes these kind of problems.
 
M

matish

gpderetta ha scritto:
If you are using GCC (or any compatible compilers) and dlopening
shared objects, make sure to read this gcc faq item:

http://gcc.gnu.org/faq.html#dso

I spent today inspecting the code I was deriving my classes from and I
discovered that there was a base class with no .cpp file but everything
contained in the header file. I must suppose that this was the reason of
the different versions of that class in different shared objects and why
the cast to it failed. I think it is somehow related to the FAQ you
cited but I don't know (and I'm asking) if having no separate definition
file is a mistake, a bad idea, or what...

ty everyone for you answers
 
P

Pavel

matish said:
Pavel ha scritto:


Should I consider it a compiler issue (that may be fixed in some future)
or something people must know when they learn C++("don't dynamic_cast
across different libraries")?

I would say it is rather the second -- a C++ user should anticipate
environment- specific issues in using classes from dynamically-linked
libraries (and not only when using dynamic_cast explicitly but with
other constructs, too, e.g. with virtual function calls). I use the
broad "environment" term to capture:

-hardware platform (CPU/memory architecture)
-os (including basic OS tool like dynamic linker)
-compiler make
-particular version of the compiler
-used compiler options
-the values of environment variables or the likes (the "environment" in
narrow sense)
-relevant dynamic libraries available on the system and their versions
-used dynamic linker's options (if the linker is called explicitly) like
in the dlopen example by gpderetta and the FAQ they pointed to.

If all above is under control of a C++ engineer, in both build and
runtime environments -- which is not impossible in some enterprise
environments -- it might be safe to deliver C++ frameworks in multiple
dynamic libraries as long as s/he knows some combination of the above
that works and it is supported by the toolset providers. "Off-the-shelf"
software manufacturers often do not have such luxury and have to
restrict themselves by using only C names from dynamically loaded
libraries (which, theoretically, can have a similar set of issues, but,
in practice, rarely do). ABI is another direction that tries to specify
some behavior / language features unspecified by the standard. One issue
with that is that the OS dynamic linker is often developed independently
of a particular C++ implementation for this same platform and has to
support a variety of options for compatibility reasons (it does not only
serve to link C++ libraries), by that limiting the scope of what could
be potentially standardized. Java is in a better position in this as its
class loader is a part of Java implementation and does not have to worry
about the rest of the host OS (with the exception of its native library
support that comes with essentially same set of issues as ? library
dynamic linking).

-Pavel
 
P

Pavel

James said:
Plus a couple of typos in the class definitions.


Or the relationship between templates and dynamic linking.
However...


I'm supposing a Unix platform (given the .so extension)---given
the way Unix does dynamic linking by default, there shouldn't be
any problems, and some quick experiments here, both under Linux
and Solaris (the latter with both g++ and Sun CC) showed no
problems. Two things he should check:

-- Exactly how the dynamic linking is being done. There could
be problems if he's using something other than the defaults
in dlopen, and doing something wrong. (In that case, he
should probably ask in a newsgroup dedicated to his system
and compiler).

-- Make sure everything has been compiled with the same version
of the headers. All too often, this sort of problem occurs
because the dynamically linked object was compiled with an
older or newer version of the class definition. (In other
words, there is a violation of the one definition rule.)

Note that this will also cause problems if the code is
statically linked. The symptoms may be different however,
but above all, since everything is linked at one time, a
good makefile will ensure that everything that is needed
gets recompiled---with dynamic linking, of course, there is
no guarantee at runtime that the library was generated using
the same sources as the main.
If the OP's intention was to compile and link "Container" and "Object"
shared libraries at the same time and with the same toolset and options
-- what sense would it make then to cerate 2 different libraries instead
of a single one? To me it would sound like asking for troubles (what if
in the future, only one library is updated -- by mistake or lack of
understanding of the supporter) without getting anything in exchange..

-Pavel
 
J

James Kanze

If the OP's intention was to compile and link "Container" and
"Object" shared libraries at the same time and with the same
toolset and options -- what sense would it make then to cerate
2 different libraries instead of a single one?

In this case, mainly to see if that is the problem.
To me it would sound like asking for troubles (what if in the
future, only one library is updated -- by mistake or lack of
understanding of the supporter) without getting anything in
exchange..

Using dynamically linked objects is asking for trouble, in a
certain sense. Sometimes, however, the advantages outweigh the
potential problems, but if you do use dynamic linking, you have
to be prepared to address what this might mean in terms of
version mismatch, etc.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

dynamic_cast and typeid 4
typeid and type_info Win32 1
dynamic cast questions 4
dynamic cast vs reinterpret_cast 9
cast 1
dynamic cast and derived classes 4
typeid ... more 6
typeid and template class 6

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,206
Latest member
SybilSchil

Latest Threads

Top