Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

dynamic_cast troubles

vpozdyayev
Beginner
2,527 Views

Hello.This code throws an exception when compiled with ICC 12.1.5 (works fine with MSVC and GCC):[cpp]class A { public: virtual void _dummy() { } }; class B: virtual public A { }; class C: virtual public A, public B { }; int main() { auto c = new C; auto a = dynamic_cast< A * >( c ); auto b = dynamic_cast< B * >( c ); auto ca = dynamic_cast< C * >( a ); // ok auto cb = dynamic_cast< C * >( b ); // std::__non_rtti_object return 0; } [/cpp] The culprit seems to be the "class B: virtual public A" part. Just "class B: public A" works without exceptions.----Regards,Vladimir

0 Kudos
27 Replies
Brandon_H_Intel
Employee
2,058 Views
I'm not catching any exceptions with this code. What compiler options are you using to build, and how exactly are you determining that the __non_rtti_object exception is being thrown?
0 Kudos
vpozdyayev
Beginner
2,058 Views
Just "icl test.cpp" produces an executable that crashes. The exception itself shows up when debugging this from under Visual Studio.
Intel C++ Compiler XE for applications running on IA-32, Version 12.1.5.344 Build 20120612
for Windows
0 Kudos
vpozdyayev
Beginner
2,058 Views
I'm not catching any exceptions with this code. What compiler options are you using to build, and how exactly are you determining that the __non_rtti_object exception is being thrown?

Hi Brandon,

were you able to reproduce the issue?

----
Vladimir

0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Vladimir, Brandon,

Did you compile the test-case without modifications? I could not compile... So, please take a look at amodified test-case:
[cpp] ... C *pC = new C; A *pA = dynamic_cast< A * >( pC ); B *pB = dynamic_cast< B * >( pC ); C *pCA = dynamic_cast< C * >( pA ); C *pCB = dynamic_cast< C * >( pB ); ...[/cpp]I'm still investigating and will provide some technical details later.

Best regards,
Sergey
0 Kudos
vpozdyayev
Beginner
2,058 Views
Sergey,

I compiled the original code sample without modifications using the ICC version specified a few posts above. Removing dependency on C++11 as per your modifications results in the same outcome.
I do not know if this problem is reproducible on other compiler versions.Which version did you use?
BTW, the issue is reproducible for both 32- and 64-bit executables.
----
Regards,
Vladimir
0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Quoting vpozdyayev
I compiled the original code sample without modifications...


Hi Vladimir,

In your original post you've stated that the test case"...works fine with MSVC...". I understood that you've compiled it with
some version of Visual Studio. Did you use 2005, 2008, 2010 or 2012 vesrion?

In case ofMicrosoft C++ compiler and Visual Studio 2005 I can't compile
...
auto c = new C;
...
because it doesn't support C++11. I simply wanted to verify howthe testworks when it is compiled with Microsoft C++ compiler.

Could you provide more details on your software development environment? Thanks in advance.

Best regards,
Sergey

PS: Here are compilation errors I have:

..\PrtTests.cpp(5710) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
..\PrtTests.cpp(5710) : error C2440: 'initializing' : cannot convert from 'C *' to 'int'

0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Hi Vladimir,

I hope thata modified test-case for a 'dynamic_case' C++ operator will be useful for you ( it is amore generic ):

...
class A
{
public:
virtual void Method( void )
{
printf( "A::Method\n" );
};
};

class B: virtual public A
{
public:
virtual void Method( void )
{
printf( "B::Method\n" );
};
};

class C: virtual public A, public B
{
virtual void Method( void )
{
printf( "C::Method\n" );
};
};

class D
{
public:
virtual void Method( void )
{
printf( "D::Method\n" );
};
};
...
C *pC = new C;

A *pA = dynamic_cast< A * >( pC );
printf( "Object A is%sinitialized\n", ( pA !=NULL ) ? " " : " NOT " );

B *pB = dynamic_cast< B * >( pC );
printf( "Object B is%sinitialized\n", ( pB !=NULL ) ? " " : " NOT " );

C *pCA = dynamic_cast< C * >( pA );
printf( "Object C is%sinitialized\n", ( pCA !=NULL ) ? " " : " NOT " );

C *pCB = dynamic_cast< C * >( pB );
printf( "Object C is%sinitialized\n", ( pCB !=NULL ) ? " " : " NOT " );

D *pD = dynamic_cast< D * >( pC );
printf( "Object D is%sinitialized\n", ( pD !=NULL ) ? " " : " NOT " );
...

0 Kudos
vpozdyayev
Beginner
2,058 Views
Hi Sergey,
the original post was about executables produced by ICC + MSVC 2010 SP1 IDE. However, the same results can be observed with ICC + MSVC 2008 IDE, and any command line combination of "(IA-32|Intel 64) Visual Studio (2008|2010) mode". OTOH, compiling this code with MSVC itself does not result in any exceptions. (For compiling with VC 2008, an "auto"-less version like the one you have suggested is necessary.)
----
Regards,
Vladimir
0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Hi Vladimir,

Have you noticed that I've added a new class D andthen tried to castthe pointerof the class Cto the pointer ofclassD?

A very important feature of the 'dynamic_cast'isthat itshould not cast fromtypeC to type D andthepointer to the object oftype D
must be equal toNULL.

However, I think that something is wrong with Intel C++ compiler because your original test-case is very generic even if it uses
some C++11 features. I wonder if thereissome problem related to support ofC++11 features in the compiler?

Best regards,
Sergey
0 Kudos
vpozdyayev
Beginner
2,058 Views
Hi Sergey.

As it appears, the issue doesn't seem to be related to C++11 support; it shows up with "C *pC = ..." instead of "auto pC = ...", as well.

An interesting point demonstrated by your code example is that the issue depends on the presence of virtual functions in different classes. I have tried a few combinations:

  • Dynamic cast from B* to C* raises an exception when B does not declare/define any virtual functions, but does NOT fail if it does (concrete or pure functions, doesn't matter).
  • Likewise, dynamic cast from C* to D* raises an exception when C declares/defines no virtual functions, and works fine otherwise.
  • The issue disappears if we add a virtual destructor to A (and, by extension, implicitly generated virtual destructors to its descendants).

It looks like ICC has a problem with empty vtables (or, rather, empty additions to vtables provided by the original classes B and C).

----
Regards,
Vladimir
0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Quoting vpozdyayev
...As it appears, the issue doesn't seem to be related to C++11 support; it shows up with "C *pC = ..." instead of "auto pC = ...", as well...

[SergeyK] I've done a verification with two more C++ compilers ( MinGW & Borland )andthere are no anyissues or problems.

An interesting point demonstrated by your code example is that the issue depends on the presence of virtual functions in different classes. I have tried a few combinations:

  • Dynamic cast from B* to C* raises an exception when B does not declare/define any virtual functions, but does NOT fail if it does (concrete or pure functions, doesn't matter).
  • Likewise, dynamic cast from C* to D* raises an exception when C declares/defines no virtual functions, and works fine otherwise.

    [SergeyK] It shouldn't raise any exceptions and it should assign a valueNULL to apointer of type D.

0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Hi Vladimir, Could you try a newtest-case?Instead of 'dynamic_cast'

Quoting Sergey Kostrov
...
C *pC = new C;


A *pA = dynamic_cast< A * >( pC );
printf( "Object A is%sinitialized\n", ( pA !=NULL ) ? " " : " NOT " );

B *pB = dynamic_cast< B * >( pC );
printf( "Object B is%sinitialized\n", ( pB !=NULL ) ? " " : " NOT " );

C *pCA = dynamic_cast< C * >( pA );
printf( "Object C is%sinitialized\n", ( pCA !=NULL ) ? " " : " NOT " );

C *pCB = dynamic_cast< C * >( pB );
printf( "Object C is%sinitialized\n", ( pCB !=NULL ) ? " " : " NOT " );

D *pD = dynamic_cast< D * >( pC );
printf( "Object D is%sinitialized\n", ( pD !=NULL ) ? " " : " NOT " );
...


use a"default" cast, like:

...

C *pC = new C;


A *pA = ( A * )pC;
printf( "Object A is%sinitialized\n", ( pA !=NULL ) ? " " : " NOT " );

B *pB = (B * )pC;
printf( "Object B is%sinitialized\n", ( pB !=NULL ) ? " " : " NOT " );

C *pCA = ( C * )pA;
printf( "Object C is%sinitialized\n", ( pCA !=NULL ) ? " " : " NOT " );

C *pCB = ( C * )pB;
printf( "Object C is%sinitialized\n", ( pCB !=NULL ) ? " " : " NOT " );

D *pD = (D * )pC;
printf( "Object D is%sinitialized\n", ( pD !=NULL ) ? " " : " NOT " );
...

0 Kudos
vpozdyayev
Beginner
2,058 Views
Hi Sergey.
C-style casts work reasonably enough, with "(C*)pA" giving a "cannot convert" compile-time error, and "(D*)pC" resulting in an unusable pointer. The trouble is only with one particular dynamic_cast, whereFindCompleteObject(void**) encounters a null pCompleteLocatorand throws the "Access violation - no RTTI data!"__non_rtti_objectexception.
----
Best regards,
Vladimir
0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Hi Vladimir,

Quoting vpozdyayev
Hi Sergey.
C-style casts work reasonably enough, with "(C*)pA" giving a "cannot convert" compile-time error, and "(D*)pC" resulting in an unusable pointer. The trouble is only with one particular dynamic_cast, whereFindCompleteObject(void**) encounters a null pCompleteLocatorand throws the "Access violation - no RTTI data!"__non_rtti_objectexception.
----
Best regards,
Vladimir


I really don't understand whyIntel C++ compilerneeds RTTI for the test-case.

0 Kudos
vpozdyayev
Beginner
2,058 Views
Hi Sergey,

this test case doesn't need RTTI and works OK. The part about "one particular dynamic_cast" was referring to the original code sample, sorry for confusion.
----
Regards,
Vladimir
0 Kudos
vpozdyayev
Beginner
2,058 Views

Gentlemen,
the original test case still fails on ICC 2013 update 2, command line (with just "icl.exe test.cpp"), or under MSVS 2012, x86 and x64. Any luck reproducing the issue?

----
Regards,
Vladimir 

0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
>>...the original test case still fails on ICC 2013 update 2, command line (with just "icl.exe test.cpp"), or under MSVS 2012, >>x86 and x64 Hi Vladimir, I regret to see that the problem is still not fixed. So, I'll do another set of tests with ICC 2013 Initial Release integrated with VS 2008 Professional Edition. Best regards, Sergey
0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
>>...I'll do another set of tests with ICC 2013 Initial Release integrated with VS 2008 Professional Edition... Here are results: There is a problem with Intel C++ Composer XE 2013 ( 2013.0.089 / Initial Release ) when the following code is executed: ... auto cb = dynamic_cast< C * >( b ); ... A 32-bit test application crashes in a malloc CRT-function ( Debug and Release configurations ) because nSize is equal to 3765269347 and this is exactly 3GB (!): ... extern "C" _CRTIMP void * __cdecl malloc( size_t nSize ) { void *res = _nh_malloc_dbg( nSize, _newmode, _NORMAL_BLOCK, NULL, 0 ); RTCCALLBACK( _RTC_Allocate_hook, ( res, nSize, 0 ) ); return res; } ... and the exception is thrown from _nh_malloc_dbg internal CRT-function. I think nSize should have a value 4. I couldn't reproduce the problem with Microsoft C++ compiler of Visual Studio 2012 Express Edition.
0 Kudos
SergeyKostrov
Valued Contributor II
2,058 Views
Here is a screenshot:
dyncastproblem.jpg
0 Kudos
vpozdyayev
Beginner
1,763 Views

Thank you, Sergey. I'm somewhat surprised to see a malloc in dynamic_cast, but the end result is the same: the "__non_rtti_object" exception. In my case (ICC 13.1.0 on MSVS 2012), the exception has been originally caused by a nullptr dereference in FindCompleteObject, just like with ICC 12.1.5.

It's a shame that, other than a single "can't reproduce" note (probably caused by using a linux version or something), Intel people seem to ignore this report. I wonder if it will take a new thread to draw attention.

0 Kudos
Reply