Casting – what could possibly go wrong?

Type casting in C++ is a form of what is known in computer science as type punning – that is, circumventing the type system of a programming language.

C++ inherits its conversion and casting mechanism from C, but supplements it (although sensibly we should say, replaces it) with four, more explicit cast operations:

  • static_cast
  • reinterpret_cast
  • const_cast
  • dynamic_cast

In C and C++ – and particularly in embedded systems – casting is a necessary evil; so much so that many programmers just accept it as part of everyday programming practice.

So then: why is casting ‘evil’? Basically, because every time you do a type cast you are opening up your program to potentially unpredictable or unexpected behaviour. Let’s have a look at the four type-cast operators and the fun and games they can unleash on the unsuspecting.

 

static_cast<>

The static_cast operator converts between different object types; for example between a double and an int. So what you are effectively saying is

“I’m about to squeeze a big object into a smaller one, so you should probably make sure the receiving object is big enough to hold the values it’s going to get.”

Or:

I’m about to force a floating point number into an integer and all those decimal places (that are probably quite important) are going to be lost”

Of course, you could also be saying:

“I’m about to put the contents of a small object into an object capable of holding much larger values (or with greater precision)”

(which is emphasising a bit of a non-problem, really)

Thankfully, C++ doesn’t let you type-cast between different class types unless you’ve defined your own explicit conversion functions (which – hopefully – should do a sensible conversion). But that’s for another time.

 

reinterpret_cast<>

reinterpret_cast is used in two ways:

  • To convert a pointer-to-type into a pointer-to-different-type
  • To convert an integer type to a pointer type; or vice versa.

When reinterpret_cast appears in code it tells the reader:

“I’m going to take the object address you gave me and treat it as a completely different type, with different memory layout and different behaviour(s). You should make sure it’s capable of supporting what I want to use it for.”

Or, in another usage:

“That (random) number you gave me? I’m going to use it as the address of an object. You’d probably better make sure it’s a valid address, in a reachable region of memory; unless you’re a big fan of segmentation faults.”

 

const_cast<>

The const_cast operator removes the const-ness of an object; that is, it makes a read-only object writeable.

Significantly for embedded programmers, const_cast removes any cv (const-volatile) qualification the original object may have. This means a volatile object – for example, one used to represent a hardware register in an embedded system – can have that qualification removed with const_cast.

Using const_cast says:

“The object you didn’t want me to change? I might (accidently) change it without your consent.”

Or, perhaps in an embedded system:

“The compiler might now optimise away any reads or writes to that object you gave me. Be prepared for behaviour NOT to happen as you expect!”

 

dynamic_cast<>

The dynamic_cast operator is a special case here, in that it is used for ‘safe down-casting’ – that is, casting a pointer-to-base-type to a pointer-to-derived-type, whilst checking whether this is, in fact, a valid cast. dynamic_cast uses Run-Time Type Identification (RTTI) to ensure the types of the pointers are valid. Thus, unlike the other cast operators, dynamic_cast is a run-time check and has associated overheads. If the pointer types are not compatible dynamic_cast returns 0 (for pointers) or throws an exception (for references).

dynamic_cast also has a role in multiple inheritance, where a class has two or more base classes. The dynamic_cast operator allows you to cast a pointer of one base class type to another. Although this is basically a variation on safe down-casting we tend to use the term ‘cross-casting’. Cross-casting is commonly encountered when a class realises (inherits from) two or more interface (pure virtual) classes.

In your code this means:

“I need to access the extended interface of a particular derived type. You’d better be prepared to deal with the consequences of the derived type NOT being what I want.”

Or:

“I need to know if the object you’ve supplied supports some other – possibly completely different – set of characteristics. ”

 

So – don’t use type casts?

Obviously, it’s impracticable (if not impossible) to write code with no type casting; especially in embedded systems. I leave you with the following guidance:

  • Don’t cast if you don’t need to.
  • Think about the consequences of what the cast is (potentially) doing.
  • Leave a big, obvious comment documenting why we’re doing something so potentially dangerous.

Posted on September 27th, 2013
» Feed to this thread
» Trackback

1 Comment a “Casting – what could possibly go wrong?”

  1. David A says:

    Thanks. This is the best explanation of the C++ type-cast operators that I have come across.

Leave a Reply