Find compile-time errors and reduce typos
Author: Andreas Fertig
Contribution – Embedded Software Engineering Congress 2018
Templates have existed in C++ for some time. With the C++11 standard update, they became even better. Now, variadic template arguments are available. Some argue that this is the most important new feature in C++11. Templates are a great way to let the compiler generate the code for you. Because templates are evaluated at compile time, they are also perfect for early error detection. You can write more robust code with them. Templates allow you to perform calculations at compile time. Together with constexpr, they are a very powerful tool that everyone should have in their toolkit.
C++ has a very strong type system. Admittedly, it is often circumvented or ignored. However, in many areas where C++ is used, this type safety plays a crucial role. It's about recognizing at compile time that, for example, an assignment is invalid or a parameter doesn't match a function call.
„Type safety means that the compiler will validate types while compiling, and throw an error if you try to assign the wrong type to a variable.“[1]
An example of a type-unsafe function is this example:
void Foo(bool first, bool addNewLine);
void UseFoo(bool first, bool addNewLine)
{
Foo(addNewLine, first);
}
void Main()
{
UseFoo(true, false);
}
Initially, it's difficult to determine the effect of the two parameters simply by looking at the `UseFoo(true, false)` call. However, upon closer inspection of the implementation, we can see that the two parameters, `first` and `addNewLine`, have been swapped within `UseFoo`. Whether this is correct or not is also difficult to ascertain at this point. Only the parameter names within the `Foo` implementation hint at the error and the actual swapping of parameters. There's absolutely no reason for the compiler to generate a warning here. Both parameters are of type `bool`, so everything seems fine. However, the impact on our program could be catastrophic. The worst part is that we only see the effect when the program is run. Therefore, the quality of our testing determines whether the error is discovered in production or only by the customer.
With just one small trick, we can improve security and detect this type of error at compile time. This is completely independent of our testing.
#define STRONG_BOOL(typeName) \
enum class typeName : bool \
{ \
No = false, \
Yes = true
}
STRONG_BOOL(First);
STRONG_BOOL(AddNewLine);
void Foo(First first, AddNewLine addNewLine);
void UseFoo(First first, AddNewLine addNewLine)
{
// Foo(addNewLine, first);
Foo(first, addNewLine);
}
void Main()
{
UseFoo(First::Yes, AddNewLine::No);
}
We create a class enum containing two values: Yes and No. This macro makes it easy to create such a class enum. As an example, we see First and AddNewLine. We then use these two new types in the function signatures of Foo and UseFoo. This prevents the parameters from being swapped. The compiler will abort the compilation process with a hard error. As an added benefit, the call to UseFoo is much clearer. Now it's immediately obvious what the two parameters represent.
Templates can be very helpful when it comes to type safety. Let's look at the following example:
int16_t max(int16_t a, int16_t b)
{
return (a > b) ? a : b;
}
int main()
{
int16_t a = 1;
uint16_t b = 65530;
printf(„max: %d „, max(a, b));
}
Here we see an implementation of the `max` function. This works with 16-bit signed integers. In the `printf` call, we pass the two parameters `a` and `b`. The program is small and clear. We can easily see that we are passing an unsigned value with the parameter `b` at this point. No problem, it compiles and links. But what is the result? We expect 65530, correct? The output of `printf` will probably surprise us with 1. This is where the integer conversion rules come into play, and the unsigned value becomes a very small signed value.
Templates can be crucial here. Let's take a look at the slightly modified program:
template
T max(T_t a, T_t b)
{
return (a > b) ? a : b;
}
int main()
{
int16_t a = 1;
uint16_t b = 65530;
printf(„max: %d „, max(a, b));
}
The `max` function was simply generalized by using a template. The effect of the template is that this program will terminate with a compile error. Templates are type-safe, and the template signature requires two parameters of the same type. No conversion rules are used here, as both types must match directly. Note that floating-point numbers may require special consideration.
Templates are a simple way to further increase the type safety of C++ and to detect errors at compile time.
Bibliography and list of sources
[1] Razin, „What is type-safe?“ https://stackoverflow.com/questions/260626/what-is-type-safe
author
Andreas Fertig studied computer science in Karlsruhe. Since his studies, he has focused on embedded systems and their associated requirements and special characteristics. Since 2010, he has worked for Philips Medical Systems as a software developer specializing in embedded systems. He has in-depth knowledge of C++. He also works freelance as a lecturer and trainer. In addition, he develops Mac OS X applications and is the author of cppinsights.io.
Implementation – our training & coaching
Do you want to bring yourself up to date with the latest technology?
Then find out more here MircoConsult offers training courses/seminars/workshops and individual coaching on the topic of implementation/embedded and real-time software development.
Training & coaching on the other topics in our portfolio can be found here. here.
Implementation – Expertise
Valuable expertise in the field of implementation/embedded and real-time software development is available. here Available for you to download free of charge.
You can find expertise on other topics in our portfolio here. here.
