Safer explicit type conversion
In C there is just one syntax for explicit type conversion: the cast expression, with syntax (T) e where T is a type and e is an expression. This notation covers a multitude of sins. It can be used to do any of the following, or more than one at the same time:
- Convert one numeric type to another
- Reinterpret a pointer to one type as a pointer to another type, an integral type as a pointer type, or vice versa
- Remove the const or volatile attribute of the expression’s type
Although C++ still supports the C-style cast syntax, it also provides better type-conversion operators with more specialised meanings:
- static_cast<T>(e) is provided to convert between numeric types
- reintrepret_cast<T>(e) is provided to convert between pointer types, or between integral and pointer types
- const_cast<T>(e) is provided to remove const and/or volatile from the type
There is also dynamic_cast<T>(e) but that is not relevant here because we’re not allowing classes to inherit from each other.
These C++ type conversion operators have significant advantages over the C cast notation. First, they make it clear what category of type conversion we intend to do. This prevents us from intending to do a type conversion in one category but accidentally doing a conversion in a more dangerous category; or inadvertantly discarding const-ness or volatility along with the conversion we intended. Second, it is easy to pick out the more dangerous categories when reviewing source code. For example, the conversions that reinterpret_cast does are generally more dangerous than the ones performed by static_cast. Third, the operand of the type conversion appears in brackets, removing the possibility of precedence errors. Fourth, you can easily search source text for instances of these type conversions.
Therefore, my “C with little bits of C++” language for developing critical systems includes the following rules:
- You are allowed to use the static_cast, reinterpret_cast and const_cast features of C++.
- You are not allowed to use the C cast notation.
For critical system development, reinterpret_cast and const_cast should generally be avoided. If you have been writing in C and using a MISRA compliance checker, then MISRA C 2004 ruless 11.1 through 11.5 restrict what you can do with C casts having the semantics of reinterpret_cast and const_cast. To get equivalent checks wheh you use the C++ constructs, you’ll need to use a MISRA C++ compliance checker, or (preferably) ban the use of reinterpret_cast and const_cast completely.
David,
The very last sentence seems to contradict rule 1 above it. Did you mean to say “To get equivalent checks when you use the C++ constructs, you’ll need to use a MISRA C++ compliance checker, or (preferably) ban the use of reinterpret_cast and //C style casts// completely.” The //…// mark the change.
Regards
Dave Banham
Dave, I did mean that const_cast should be banned, because removing the const-attribute from a type is not something that should normally be done. My subset already bans C-style casts – see rule 2. On reflection, I can see that you might want to use const_cast to remove the volatile attribute – for example when you have disabled interrupts, or otherwise suspended other operations that might mutate the value concerned. What I meant by allowing reinterpret_cast and const_cast in rule 1 is that if you really must do dangerous type conversions, then you should be honest about it. So I’m suggesting that C style casts should be banned absolutely, and reinterpret_cast and const_cast should be avoided unless absolutely necessary, with any instances of them justified in the documentation.
David,
static_cast is not always safe, even from a memory safety point of view. As described in http://www.cppreference.com/wiki/keywords/static_cast, static_cast can be used to cast pointers down the hierarchy without even a dynamic check. So static_cast can be more dangerous here than a dynamic_cast!
I guess you just don’t consider these as you ban inheritance (and so hierarchies).
That’s right – but as you say, I haven’t introduced inheritance into my C++ subset yet. If I ever do, then the correctness of that use of static_cast would need to be formally verified.