dynamic_cast troubles

dynamic_cast troubles

Hello.This code throws an exception when compiled with ICC 12.1.5 (works fine with MSVC and GCC):

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; } 
The culprit seems to be the "class B: virtual public A" part. Just "class B: public A" works without exceptions.----Regards,Vladimir

28 posts / novo 0
Último post
Para obter mais informações sobre otimizações de compiladores, consulte Aviso sobre otimizações.

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?

Brandon Hewitt Technical Consulting Engineer For 1:1 technical support: http://premier.intel.com Software Product Support info: http://www.intel.com/software/support

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 20120612for Windows

Quoting Brandon Hewitt (Intel)
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

Vladimir, Brandon,

Did you compile the test-case without modifications? I could not compile... So, please take a look at amodified test-case:

     ...

     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 );

     ...
I'm still investigating and will provide some technical details later.

Best regards,
Sergey

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

Quoting vpozdyayevI 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'

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 " );
...

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

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

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

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.

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 " );
...

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

Hi Vladimir,

Quoting vpozdyayevHi 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.

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

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 

>>...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

>>...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.

Here is a screenshot:

Anexos: 

AnexoTamanho
Download dyncastproblem.jpg18.77 KB

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.

>>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.

I didn't repeat a test with ICC 12 but I confirm that I saw some processing with FindCompleteObject internal function when I was doing debugging with ICC 13 and VS 2008 PE.

Brandon,

I can see that some time ago you've made a statement "...I'm not catching any exceptions with this code....". So, what else should we do in order to prove that there is a problem? Please let us know.

Best regards,
Sergey

Hi all,

Sorry for missing the updates on this thread. With the extra information, I could reproduce the thrown RTTI exception, and I've submitted a problem report to our front-end team.  Interestingly, if class B contains its own virtual function, the exception no longer gets thrown.

Brandon Hewitt Technical Consulting Engineer For 1:1 technical support: http://premier.intel.com Software Product Support info: http://www.intel.com/software/support

What happened to the 1st original post? As soon as Brandon made his post the original post is re-formatted to almost unreadable form.

>>... Interestingly, if class B contains its own virtual function, the exception no longer gets thrown...

Hi Brandon, Do you mean a modification like this one:
...
class B : virtual public A
{
public:
virtual void _dummy() { }
};
...

Thanks, Brandon.

> Interestingly, if class B contains its own virtual function, the exception no longer gets thrown

That's right; as I have mentioned earlier, this problem seems to depend on whether B has any additions to vtable. I am currently using a virtual destructor in A as a workaround (which results in automatic generation of default virtual ~B() for all B classes).

----
Regars,
Vladimir 

>>>> Interestingly, if class B contains its own virtual function, the exception no longer gets thrown
>>
>>That's right; as I have mentioned earlier, this problem seems to depend on whether B has any additions to vtable. I am
>>currently using a virtual destructor in A as a workaround (which results in automatic generation of default virtual ~B() for
>>all B classes).

Even if both "tricks" could be considered as workarounds the initial test case ( in post #1 ) should work without throwing the exception.

Best regards,
Sergey

Hi all,

The original issue with a std::__non_rtti_object exception getting thrown is resolved in the latest 13.1 and 14.0 compilers (Intel(R) C++ Composer XE 2013 and 2013 SP1 products). Let me know if you have any further issues.

Brandon Hewitt Technical Consulting Engineer For 1:1 technical support: http://premier.intel.com Software Product Support info: http://www.intel.com/software/support

Deixar um comentário

Faça login para adicionar um comentário. Não é membro? Inscreva-se hoje mesmo!