473,566 Members | 2,924 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

[Q] Strange dynamic_cast problem

Ok...this seems to be treading into some really esoteric areas of c++.
I will do my best to explain this issue, but I don't fully understand
what is happening myself. I am hoping that the problem comes down to
standard c++ stuff and is not specific to Mac OS X compiler&linker .

I have put together a simple test project which can be found at:

http://ericgorr.net/LibraryLoading.zip

which demonstrates the problem.

In Shared.h, there are the definitions of two purely virtual classes -
A & B. B is a subclass of A.

In LibraryA, there is a implementation of class B called BImp. There
is a function called GetA which returns a pointer to an instance of
BImp and returns an A*.

In LibraryB, there is a function called test. This function takes a
void *, which will end up being a function pointer to the GetA
function from LibraryA.

The problem comes in the test function with the line:

B* myB = dynamic_cast<B* (myA);

The dynamic_cast fails and myB is assigned NULL. This _should not_
fail because myA is an instance of class B.

However, I can make this dynamic_cast succeed, if in main.cp, which is
a part of the Application target, I set:

#define CASE_A 1

which allows

A *myA = functionPtr();
B *myB = dynamic_cast<B* (myA);

to be executed before the test function from LibraryB is called.

Any idea why this allows it to work?
Any idea why it is failing in the first place?

In main.cp, there are two #define's.

#define CASE_A 0
#define CASE_B 0

If both are set to zero, it will crash. This is simplest crashing
case.
If CASE_A is 1, it will work. Any idea why?
If CASE_B is 1, it will crash and this is the case closest to the real
case I am working with.

Since I started composing this message, I've been looking into the
issue a bit more and thought that if it were possible to make the
destructors pure virtual functions as well, then that would solve the
problem.

Googling for "pure virtual destructors c++", I came across:

http://www.linuxtopia.org/online_boo...ter15_024.html

While it seems strange, apparently this is allowed in C++.

So, if I changed Shared.h to look like:

*****
*****
#define VISIBLE __attribute__ ((visibility("d efault")))

class VISIBLE A
{
public:
virtual ~A( void ) = 0;
virtual void Func( void ) = 0;
};
A::~A() {}
class VISIBLE B : public A
{
public:
virtual ~B( void ) = 0;
virtual void Func1( void ) = 0;
};
B::~B() {}
extern "C" VISIBLE A *GetA( void );
*****
*****

everything worked in all three cases.

Any comments on this solution? Any reason why this wouldn't be
perfectly valid?

Any thoughts or comments would be appreciated.

Nov 4 '08 #1
18 5265

You're more likely to get a good response
if you post a short, compilable example
that exhibits the problem.

Make it easy for us to help you!

Sean

Nov 4 '08 #2
On Nov 4, 2:18*pm, sean_in_rale... @yahoo.com wrote:
You're more likely to get a good response
if you post a short, compilable example
that exhibits the problem.

Make it easy for us to help you!
This is what:

http://ericgorr.net/LibraryLoading.zip

is supposed to be. I realize that it is Mac specific and that this
will limit the number of people who will be able to help, but there
really isn't much code there at all if you wanted to take a look.

In a different forum, someone pointed out that:

-----
All the C++ specification specifies is what happens in a set of C++
source modules, all linked together to form one application. Nowhere
is it discussed what happens if you have DLL's, shared libraries, or
anything else with respect to the language.
-----

which does actually help. In the 'real' case, this is working just
fine within Visual Studio.

If how this works is basically undefined, there will not be a 'right'
solution - or the 'right' solution will be platform specific. In which
case, I will need to deal exclusively with Apple and their forums.
Nov 4 '08 #3
On Nov 4, 2:18*pm, sean_in_rale... @yahoo.com wrote:
You're more likely to get a good response
if you post a short, compilable example
that exhibits the problem.

Make it easy for us to help you!
This is what:

http://ericgorr.net/LibraryLoading.zip

is supposed to be. I realize that it is Mac specific and that this
will limit the number of people who will be able to help, but there
really isn't much code there at all if you wanted to take a look.

In a different forum, someone pointed out that:

-----
All the C++ specification specifies is what happens in a set of C++
source modules, all linked together to form one application. Nowhere
is it discussed what happens if you have DLL's, shared libraries, or
anything else with respect to the language.
-----

which does actually help. In the 'real' case, this is working just
fine within Visual Studio.

If how this works is basically undefined, there will not be a 'right'
solution - or the 'right' solution will be platform specific. In which
case, I will need to deal exclusively with Apple and their forums.
Nov 4 '08 #4
On Nov 4, 11:58*am, Eric <ericg...@gmail .comwrote:
Ok...this seems to be treading into some really esoteric areas of c++.
I will do my best to explain this issue, but I don't fully understand
what is happening myself. I am hoping that the problem comes down to
standard c++ stuff and is not specific to Mac OS X compiler&linker .

I have put together a simple test project which can be found at:

*http://ericgorr.net/LibraryLoading.zip

which demonstrates the problem.

In Shared.h, there are the definitions of two purely virtual classes -
A & B. B is a subclass of A.

In LibraryA, there is a implementation of class B called BImp. There
is a function called GetA which returns a pointer to an instance of
BImp and returns an A*.

In LibraryB, there is a function called test. This function takes a
void *, which will end up being a function pointer to the GetA
function from LibraryA.

The problem comes in the test function with the line:

* * * * B* myB = dynamic_cast<B* (myA);

The dynamic_cast fails and myB is assigned NULL. This _should not_
fail because myA is an instance of class B.
prove it:

#include <iostream>
#include <typeinfo>

....
// should print a mangled representation of class B
std::cout << typeid(myA).nam e() << std::endl;

>
However, I can make this dynamic_cast succeed, if in main.cp, which is
a part of the Application target, I set:

#define CASE_A 1

which allows

* * * * * * * * A *myA = functionPtr();
* * * * * * * * B *myB = dynamic_cast<B* (myA);

to be executed before the test function from LibraryB is called.

Any idea why this allows it to work?
Any idea why it is failing in the first place?

In main.cp, there are two #define's.

#define CASE_A 0
#define CASE_B 0

If both are set to zero, it will crash. This is simplest crashing
case.
If CASE_A is 1, it will work. Any idea why?
If CASE_B is 1, it will crash and this is the case closest to the real
case I am working with.

Since I started composing this message, I've been looking into the
issue a bit more and thought that if it were possible to make the
destructors pure virtual functions as well, then that would solve the
problem.

Googling for "pure virtual destructors c++", I came across:

http://www.linuxtopia.org/online_boo...s/thinking_in_...

While it seems strange, apparently this is allowed in C++.

So, if I changed Shared.h to look like:

*****
*****
#define VISIBLE __attribute__ ((visibility("d efault")))

class VISIBLE A
{
public:
* * virtual ~A( void ) * * * * *= 0;
* * virtual void Func( void ) * = 0;};

A::~A() {}

class VISIBLE B : public A
{
public:
* * virtual ~B( void ) * * * * *= 0;
* * virtual void Func1( void ) *= 0;};

B::~B() {}

extern "C" VISIBLE A *GetA( void );
*****
*****

everything worked in all three cases.

Any comments on this solution? Any reason why this wouldn't be
perfectly valid?

Any thoughts or comments would be appreciated.
Whether the destructor is virtual or not won't matter, unless that
d~tor were the only function available. All you need is one virtual
function in a base class.

A dynamic_cast will fail if:

a) the types involved are not polymorphic: base has at least one
virtual member function
b) the compiler is not configured to store RTTI (Real Time Type
Information)
c) the object *at* the dynamic_cast's arguement is a Parent type of
the lhv.

Base base;
Base* p_base = &base;
Derived* p_d = dynamic_cast< Derived* >(p_base); // failure

So the following should run with no errors:

#include <iostream>
#include <stdexcept>

class Base
{
virtual void test() {}
};

class Derived: public Base
{
void test() {}
};

int main ()
{
try
{
Derived derived;
Base* p_base = &derived;
Derived* p_der = dynamic_cast< Derived* >(p_base);
if (p_der == 0)
throw std::runtime_er ror("failed dynamic_cast< Derived* >");
}
catch(const std::exception& e)
{
std::cout << "Error: ";
std::cout << e.what() << std::endl;
}
}

No need for a virtual d~tor since i'm not allocating and deallocating
manually using Base* anywhere. I say your problem is b), but thats a
dice roll.
Nov 4 '08 #5
On Nov 4, 2:40*pm, Salt_Peter <pj_h...@yahoo. comwrote:
On Nov 4, 11:58*am, Eric <ericg...@gmail .comwrote:
Ok...this seems to be treading into some really esoteric areas of c++.
I will do my best to explain this issue, but I don't fully understand
what is happening myself. I am hoping that the problem comes down to
standard c++ stuff and is not specific to Mac OS X compiler&linker .
I have put together a simple test project which can be found at:
*http://ericgorr.net/LibraryLoading.zip
which demonstrates the problem.
In Shared.h, there are the definitions of two purely virtual classes -
A & B. B is a subclass of A.
In LibraryA, there is a implementation of class B called BImp. There
is a function called GetA which returns a pointer to an instance of
BImp and returns an A*.
In LibraryB, there is a function called test. This function takes a
void *, which will end up being a function pointer to the GetA
function from LibraryA.
The problem comes in the test function with the line:
* * * * B* myB = dynamic_cast<B* (myA);
The dynamic_cast fails and myB is assigned NULL. This _should not_
fail because myA is an instance of class B.

prove it:

#include <iostream>
#include <typeinfo>

...
// should print a mangled representation of class B
std::cout << typeid(myA).nam e() << std::endl;
The getMyA function only has a single line of code:

return new BImp;

and BImp is subclass of B which is a subclass of A.

Of course, typeid(myA).nam e() does not print a mangled representation
of class B (it prints 'P1A'), but this is the problem I am trying to
solve.

I don't know why (for certain) this does not work. Looking at the code
alone and stepping through it with the debugger, it is trivial to see
that it should be working. But when the dynamic_cast it hit, it fails.

I am beginning to think this is an OS specific issue as I mentioned in
a reply to sean.

A dynamic_cast will fail if:

b) the compiler is not configured to store RTTI (Real Time Type
Information)
I say your problem is b), but thats a
dice roll.
RTTI is configured properly.

thank you for your comments.
Nov 4 '08 #6
On Nov 4, 2:40*pm, Salt_Peter <pj_h...@yahoo. comwrote:
On Nov 4, 11:58*am, Eric <ericg...@gmail .comwrote:
Ok...this seems to be treading into some really esoteric areas of c++.
I will do my best to explain this issue, but I don't fully understand
what is happening myself. I am hoping that the problem comes down to
standard c++ stuff and is not specific to Mac OS X compiler&linker .
I have put together a simple test project which can be found at:
*http://ericgorr.net/LibraryLoading.zip
which demonstrates the problem.
In Shared.h, there are the definitions of two purely virtual classes -
A & B. B is a subclass of A.
In LibraryA, there is a implementation of class B called BImp. There
is a function called GetA which returns a pointer to an instance of
BImp and returns an A*.
In LibraryB, there is a function called test. This function takes a
void *, which will end up being a function pointer to the GetA
function from LibraryA.
The problem comes in the test function with the line:
* * * * B* myB = dynamic_cast<B* (myA);
The dynamic_cast fails and myB is assigned NULL. This _should not_
fail because myA is an instance of class B.

prove it:

#include <iostream>
#include <typeinfo>

...
// should print a mangled representation of class B
std::cout << typeid(myA).nam e() << std::endl;
The getMyA function only has a single line of code:

return new BImp;

and BImp is subclass of B which is a subclass of A.

Of course, typeid(myA).nam e() does not print a mangled representation
of class B (it prints 'P1A'), but this is the problem I am trying to
solve.

I don't know why (for certain) this does not work. Looking at the code
alone and stepping through it with the debugger, it is trivial to see
that it should be working. But when the dynamic_cast it hit, it fails.

I am beginning to think this is an OS specific issue as I mentioned in
a reply to sean.

A dynamic_cast will fail if:

b) the compiler is not configured to store RTTI (Real Time Type
Information)
I say your problem is b), but thats a
dice roll.
RTTI is configured properly.

thank you for your comments.
Nov 4 '08 #7
Eric wrote
In LibraryB, there is a function called test. This function takes a
void *, which will end up being a function pointer to the GetA
function from LibraryA.

The problem comes in the test function with the line:

B* myB = dynamic_cast<B* (myA);

The dynamic_cast fails and myB is assigned NULL. This _should not_
fail because myA is an instance of class B.
A lot of people make the mistake of casting in and out of void* using
different types. This will cause UB. The reason being can be seen in
4.10.2:

The result of converting a "pointer to cv T" to a "pointer to cv void"
points to the start of the storage location where the object of type T
resides, as if the object is a most derived object (1.8) of type T (that
is, not a base class subobject).

In other words, if you cast a B* to a void*, then cast that void* to an
A* (or visa-versa) you're going to have serious problems. The A* will
now, possibly incorrectly, point to a location in memory that is a B*
without the correct interpretation step that would normally transpire in
a static_cast. The result of accessing this pointer in any way is then UB.

If you're using MI you are GOING to have problems here. Otherwise you
may or may not.

I solve this problem in our shop by disallowing the passing of variables
through a void* without a type-safe wrapper like boost::any. This makes
sure that the pointers on both sides of the cast will be the exact same
type, as they must be to avoid UB.
Nov 4 '08 #8
On Nov 4, 4:42*pm, Noah Roberts <u...@example.n etwrote:
Eric wrote
In LibraryB, there is a function called test. This function takes a
void *, which will end up being a function pointer to the GetA
function from LibraryA.
The problem comes in the test function with the line:
* *B* myB = dynamic_cast<B* (myA);
The dynamic_cast fails and myB is assigned NULL. This _should not_
fail because myA is an instance of class B.

A lot of people make the mistake of casting in and out of void* using
different types. *This will cause UB. *The reason being can be seen in
4.10.2:

The result of converting a "pointer to cv T" to a "pointer to cv void"
points to the start of the storage location where the object of type T
resides, as if the object is a most derived object (1.8) of type T (that
is, not a base class subobject).

In other words, if you cast a B* to a void*, then cast that void* to an
A* (or visa-versa) you're going to have serious problems. *The A* will
now, possibly incorrectly, point to a location in memory that is a B*
without the correct interpretation step that would normally transpire in
a static_cast. *The result of accessing this pointer in any way is thenUB.

If you're using MI you are GOING to have problems here. *Otherwise you
may or may not.

I solve this problem in our shop by disallowing the passing of variables
through a void* without a type-safe wrapper like boost::any. *This makes
sure that the pointers on both sides of the cast will be the exact same
type, as they must be to avoid UB.
Just to be clear, none of the variables are ever passing through a
void*.

The only use of a void* in the sample code is to pass around a pointer
to a function. In every case, the function is always successfully
passed and called.

Again, it is unlikely the usage of the void* in the code is the cause
of this problem.

Any other suggestions people have are always appreciated.
Nov 4 '08 #9
On Nov 4, 4:42*pm, Noah Roberts <u...@example.n etwrote:
Eric wrote
In LibraryB, there is a function called test. This function takes a
void *, which will end up being a function pointer to the GetA
function from LibraryA.
The problem comes in the test function with the line:
* *B* myB = dynamic_cast<B* (myA);
The dynamic_cast fails and myB is assigned NULL. This _should not_
fail because myA is an instance of class B.

A lot of people make the mistake of casting in and out of void* using
different types. *This will cause UB. *The reason being can be seen in
4.10.2:

The result of converting a "pointer to cv T" to a "pointer to cv void"
points to the start of the storage location where the object of type T
resides, as if the object is a most derived object (1.8) of type T (that
is, not a base class subobject).

In other words, if you cast a B* to a void*, then cast that void* to an
A* (or visa-versa) you're going to have serious problems. *The A* will
now, possibly incorrectly, point to a location in memory that is a B*
without the correct interpretation step that would normally transpire in
a static_cast. *The result of accessing this pointer in any way is thenUB.

If you're using MI you are GOING to have problems here. *Otherwise you
may or may not.

I solve this problem in our shop by disallowing the passing of variables
through a void* without a type-safe wrapper like boost::any. *This makes
sure that the pointers on both sides of the cast will be the exact same
type, as they must be to avoid UB.
Just to be clear, none of the variables are ever passing through a
void*.

The only use of a void* in the sample code is to pass around a pointer
to a function. In every case, the function is always successfully
passed and called.

Again, it is unlikely the usage of the void* in the code is the cause
of this problem.

Any other suggestions people have are always appreciated.
Nov 4 '08 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

13
4946
by: GianGuz | last post by:
Everyone knows about the complex and cpu-expensive procedures taken by dynamic_cast to find the right function call in a virtual classes hierarchy. The question I would to rise is when dynamic_cast is really necessary? A wise usage of templates and/or non dynamic_cast operators can grant the compiler with any information it needs to...
8
2583
by: Thomas Lorenz | last post by:
Hello, first, I didn't find any reference to my question through googling. dynamic_cast uses RTTI to determine if the cast expression is valid. Invalid casts of pointers give a '0'-result. As it is the case in the following example: ----------------------------------------------------------- #include <iostream>
2
1747
by: yccheok | last post by:
Hello, I have an object called XXX previously derived from CDocument in my MDI project. Later, I create an concrete class called Subject. And I let XXX to have multiple inheritance from Subject (Subject is an object with protected constructor) The problem is, whenever I used dynamic_cast to cast a Subject pointer to XXX (I am quite sure...
5
2028
by: tthunder | last post by:
Hi @all, Perhaps some of you know my problem, but I had to start a new thread. The old one started to become very very confusing. Here clean code (which compiles well with my BCB 6.0 compiler). You can find a problem there, which cannot be solved by dynamic_cast, because the pointers can be NULL, and I only want to know, if the pointer type...
22
4789
by: Boris | last post by:
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...
5
2995
by: mijobee | last post by:
Hello Everyone, I just wanted to check that I'm using dynamic_cast correctly. I have a hierarchy of objects using pure virtual classes and virtual inheritance to implement interfaces. I ran into a problem using C-style casting to an instance of a derived class from its interface type to a variable of its concrete type. I was confused...
15
2811
by: Grizlyk | last post by:
Hello. Returning to question of manual class type identification, tell me, for ordinary inheritance is C++ garantee that dynamic_cast<Derived*>(Base*) can be implemented similarly to return (Base*->type_fild >= Derived_typeid)? Base*: 0;
15
12279
by: Bo Yang | last post by:
Hi, I can understand static_cast, reinterpret_cast and const_cast, they all work at compile time. But I can figure out how the C++'s dynamic- cast works? Could you please explain how for me? Thanks in advance! Regards! Bo
0
7673
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main...
0
7584
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language...
0
7893
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. ...
0
8109
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that...
0
7953
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the...
0
3643
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in...
0
3626
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
1202
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
926
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.