The Airsource Blog

Why should I use static_cast?

I recently had the dubious pleasure of debugging a User 42 Panic on a piece of Symbian code that was given to me by another company. You always need to make sure you understand what the system is telling you, so I went straight to the documentation:

User 42: This panic is raised by a number of RHeap member functions, AllocLen(), Free(), FreeZ(), ReAlloc(), ReAllocL(), Adjust() and AdjustL() when a pointer passed to these functions does not point to a valid cell.

Hmmm. That didn't really give me many clues. Well, I needed to figure out how to reproduce the bug anyway. That was relatively easy, I could reproduce it with a User::Leave(). The leave was being trapped, and the debugger showed the panic was being issued between the Leave and the TRAP, so that pointed to a problem on the CleanupStack (which agreed with what the documentation said). Isolating the problem even further showed that I could reproduce the problem by doing this:

    CItemBase* item = (CItemBase*)decoder->ReadItemLC(); 
    CleanupStack::PopAndDestroy(item);

For reference, ReadItemLC() constructed an MDecodable, and the inheritance hierarchy looked like:

MultipleInheritance

Ignoring the fact that a function called ReadItemLC() should return a CItemBase*, not an MDecodable*, the problem here is that in that inheritance hierarchy, both CItemBase and MDecodable have their own data (MDecodable has a vtable pointer, even though it's an abstract class). Consequently when you cast a pointer from an MDecodable* to a CItemBase* you get a different pointer value. Try the attached code on any system for a demonstration of the problem.

However, in the code sample above, the CItemBase class had not been fully defined; we had simply forwarded-declared CItemBase. That meant the compiler couldn't do the necessary arithmetic (subtract 4) to convert an MDecodable* to a CItemBase* - because it didn't know whether they were actually part of the same inheritance tree, or completely dissociated types. So it did the equivalent of a reinterpret_cast.

When I changed the code to do the correct C++ style cast:

    CItemBase* item = static_cast<CItemBase*>(decoder->ReadItemLC()); 
    CleanupStack::PopAndDestroy(item);

It promptly failed to compile, because CItemBase hadn't been fully defined. Adding

#include "ItemBase.h"

at the top of the file fixed the compile - and fixed my User 42 Panic as well.

Lesson: next time I get code from another source, check the casting is done properly.