Bugs I Often Write in C++
Every time I try to code a project in C++ I run into the same bugs, and I keep forgetting how to solve them. So, I decided to write this list here for my own reference.
Eigen initialization bug
Symptoms: The code compiles and runs properly, but the output seems random and changes from one run to the next, despite the code being theoretically deterministic.
Diagnosis: This happens because you am declaring a variable and calling it without initializing it. For example, Eigen::Matrix<int,3,5> A;
will not create a matrix A
of zeros, instead it will fill with entries with garbage from the computer’s memory.
Treatment: Go variable by variable and ensure that it is being properly initialized after being declared. For example, instead of Eigen::Matrix<int,3,5> A;
, write
Eigen::Matrix<int,3,5> A;
A.setZero();
Linker error
Symptoms: The code seems to compile well but breaks just at the end of the compilation process, returning an error like ld: symbol(s) not found for architecture x86_64
.
Diagnosis: This happens because you are not compiling one of the source files you’re using. For example, you wrote main.cpp
which itself includes another function you wrote in func.cpp
,func.h
, but you are only compiling main
and not func
.
Treatment: If you are using CMake (and func
is in a path that CMake checks for source code, e.g., include/
or /src
if you are using CMake best practices), remove all CMake cache and build the project again,
rm -rf build
mkdir build
cd build
cmake ../
If you are not using CMake (even though you should) and instead compiling by calling your compiler directly from the terminal like gcc main.cpp -o main
, then add func.cpp
to the source files being compiled by calling instead gcc main.cpp func.cpp -o main
.
Multiple inclusions
Symptoms: Compiling fails with errors like error: redefinition of ...
or ... included multiple times
.
Diagnosis: This happens because, when compiling, the compiler is reaching the same function or variable definition many times. For example, say we have a function main.cpp
:
#include "func.h"
#include "func2.h"
int main(int argc, char *argv[])
{
func(0);
func2(0);
}
which calls functions func.cpp
#include "func.h"
void func(int a){ std::cout << a << std::endl; }
with header file func.h
:
void func(int a)
and
func2.cpp
#include "func2.h"
void func2(int a){ func(a+1); }
with header file func2.h
:
#include "func.h"
void func2(int a)
When the compiler sees an include
statement, it will literally just copy that file at that position in the code; and, of course, this works recursively. Therefore, since main
is including func.h
and func2.h
, which in turn also includes func.h
, when the compiler starts reading main.cpp
and sees
#include "func.h"
#include "func2.h"
it will interpret it as
void func(int a)
#include "func2.h"
and then
void func(int a)
#include "func.h"
void func2(int a)
which recursively turns into
void func(int a)
void func(int a)
void func2(int a)
Thus, even without knowing it, we were defining the same function twice to the compiler’s eyes, and that makes it crash.
Treatment: The best way to be protected against this is always using include guards, a smart trick to ensure that the compiler will only enter the main text of each header file once. Basically, envelop every header file you ever write in an if statement like this:
#ifndef UNIQUE_NAME
#define UNIQUE_NAME
// header code here
#endif
Make sure that UNIQUE_NAME
is unique for each header file and it never shares a name with any file or variable or functions ever read by the compiler. For example, you can make them all caps and make sure you never write all caps variable or function names.
So, in our example, we would make func.h
#ifndef FUNC
#define FUNC
void func(int a)
#endif
and func2.h
#ifndef FUNC2
#define FUNC2
#include "func.h"
void func2(int a)
#endif
By doing this, we avoid the compiler entering the same code twice and we avoid the error
Apple SDK mismatch
Symptoms: The code compiles successfully, but when ran returns an error that references the Apple command line tools, like:
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSString.h:402:114: error: function does not return string type
- (nullable instancetype)initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding NS_FORMAT_ARGUMENT(1);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObjCRuntime.h:103:48: note: expanded from macro 'NS_FORMAT_ARGUMENT'
#define NS_FORMAT_ARGUMENT(A) __attribute__ ((format_arg(A)))
Diagnosis: This (I think!) happens because there is a mismatch between the versions of your operating system and the Apple Command Line Tools. This is caused by Apple itself sometimes (i.e., having the latest version of both does not necessarily solve this).
Treatment: I solve this by specifying a different compiler, by adding
set(CMAKE_CXX_COMPILER "/usr/bin/g++")
set(CMAKE_C_COMPILER "/usr/bin/gcc")
to my CMakeLists.txt
. I am not really sure why this fixes this, but it does 🤷