Compiler warnings can be a nuisance, clogging the output with unnecessarily
verbose information.
This is especially true of C++, even more so when the code uses template magic.
However, turning them off can be rather harmful.
TL;DR
Set -Wall -Wextra, or equivalent, as your basic compiler flags, both in debug
and in release mode.
If you are using CMake, the Autocmake
project does it by
default.
Consider the following C++11 example, but it applies equally well to earlier
standards.
The
cnpy library for saving/loading C++ arrays
to/from NumPy binary format has a basic data structure, called NpyArray.
An object of this type is constructed by supplying:
the shape of the array, i.e. a std::vector<size_t>. For example, a
2-dimensional array with dimensions 10 and 11 will have: std::vector<size_t>
shape({10, 11});
the word size of the data type to be dumped to NumPy array. This is
either read from the .npy when loading the array, or determined by the
result of sizeof(T), where T is the data type, when saving the array.
The constructor will then compute how large a memory buffer is needed and
allocate a std::vector<char>.
The number of values in the array is computed from the shape array:
or more compactly using std::accumulate:
The type information is encoded in the .npy
file format header. When loading the array the user will have to perform a
reinterpret_cast to get the correct data type.
Runtime error!
Stripped to its barebones, the NpyArray class looks like this:
Let’s now try to compile it:
The live example on Coliru shows that we get a runtime error
because the assertion in the constructor fails.
What’s happening? Well, a very, very stupid mistake. The buffer_ data member is initialized using the nValues_ data member
This shouldn’t be a problem, since it’s initialized first in the constructor, right?
Wrong! According to the standard, 12.6.2 Section 13.3
data members are initialized in the order they were declared in the class. Thus buffer_ gets initialized first, using an undefined value for nValues_.
Fixing it
The correctstruct declaration is thus:
which also honors the tenet of ordering data members in your classes and structs by their size in memory.
However, this is something very easily forgotten. How to avoid these kinds of errors?
Do not initialize data members based on other data members. This is, in
my opinion, overly restrictive.
Insert assertions in the constructors. Very useful, but assertions only
work when -DNDEBUG is not given to the compiler. Most of the times this is not the case
when compiling with optimization.
Turn on compiler warnings. -Wall catches this mistake and many others. For an extra layer
of warnings, I also turn on -Wextra. This is the output on Coliru