Embracing Compiler Errors For Fun And Profit
You have just finished a marathon coding session. Time to hit the good ol' compile button. Somewhat smugly you sit back waiting to run this new and improved code. Only, you are greeted by 3 pages worth of compiler errors. The sadness is real. But, the rewards will be ever so great!
Far too many times have I seen code compile with warnings after warnings filling the TTY1 and then happily run and shipped. And more often than not the warnings have been disabled because they are “annoying”; after all, they are not really errors anyway, right? That was certainly my attitude when I first started writing C code many eons ago. “If they had been actual problems, the compiler would’ve given me an error”, I firmly declared.
I was naively wrong, of course.
Why
The thing with warnings is that they highlight real problems with code. Sometimes they only show up when compiling the code on a different platform, indicating a portability problem.
Fixing warnings will help improve code quality, help alleviate potential cross platform issues, and produce more stable code. Because of this, rather than disabling them, you want to enable as many warnings as possible, and in order to not let them go unnoticed you want to treat them as errors.
Scary!
Different compilers use different flags for dealing with warnings and errors but they generally support the same level of warnings. For the purpose of this post I’ll just work with the clang compiler.
How
At the very least you want to enable all warnings. Yes, all warnings! At least!
You do that with -Wall
. Now, this is not truly all warnings that
clang reports, but it has all the warnings that have high value and low false
positives.
What does that mean? High value means that they are reporting on actual problems and low false positives means they are reported in a context where they are most likely true as well.
The next step you want to do is to treat them as errors. This is done with the
-Werror
flag. With that enabled you will no longer get any warnings (yay!),
you will instead only get errors (doh!).
Next we want to enable some extra warnings, because the more the merrier! These
are the kind that still have high value (points at actual problems), but may
have slighty more false positive reported. We enable that with -Wextra
. This
will provide warnings for things like comparisons between different signs, etc.
Since we’re all pedantic programmers we can rejoice in the fact that the
compiler has just the flag for us: -pedantic
. This tell the compiler to warn
(and thus give us an error) if we compile any code that the C++ Standard (in our
case) says is forbidden; usually various compiler language extensions. This
works against the current standard you compile against, for instance:
-std=c++14
.
Our compiler flags now look like this: -Wall -Wextra -Werror -pedantic
This is the bare minimum we want to compile all our code with. There are some caveats to be aware of though.
When
The world is seldom black and white, and so it is with compiler warnings too. A large codebase is usually made up from a mix of your own code and a variety of third party libraries. Some of these libraries might even be compiled as part of your build process, as opposed to being linked in as libraries. In such cases you really only want to enable aggressive compiler warnings for code that you directly control, i.e the code you write yourself. You don’t really want to spend time fixing third party code, you want to focus on our own. Though I personally believe that any warnings that arise from compiling third party code should be reported as bugs (within reason).
There’s also the case where some warnings are actually “harmless”, or even
reported in error. For instance, the following code give a warning under -Wall
:
std::array<int, 2> a { 1, 2 };
error: suggest braces around initialization of subobject
[-Werror,-Wmissing-braces]
The above construct is perfectly valid, yet we get a warning2. In cases like
this we can selectively disable warnings. For this example we can add
-Wno-missing-braces
to our compiler flags to silence it.
For this reason we need to make a judgement call about each warning we get whether it’s a real problem for our code base. The general advice is to err on the side of caution when it comes to disabling warnings though, and leave as many as possible on and simply fix the code.
Summary
Dealing with compiler errors and warnings is always a pain, but we can take comfort in the fact that it’s better to have compiler errors than runtime errors. And by enabling as many warnings as possible and treating them as errors, and subsequently fixing them, we robustify our code and help reduce the chances of runtime errors. This leads to better code quality and cleaner code; something we should always strive for!
- Compile with the equivalent of at least:
-Wall -Wextra -Werror -pedantic
- Disable the warnings that are “harmless”, or even wrong (report them as bugs)
- Enabling all warnings and treating them as errors improves code quality
- Enabling all warnings and treating them as errors will lead to clean code
- Use for code that you have direct control over