Compilation Using g++¶
Compilation is a process to translate source codes into executables or libraries. A compiled programming language requires source codes to be compiled before they can be executed. C++ is typical compiled programming language.
Mainstream Compilation Systems¶
There are many mainstream C/C++ compilers available to various operating systems:
Cross-platform (Linux native, Mac OS, Windows, etc.)
gcc/g++ from GNU GCC
Mac OS native
The LLVM C/C++ compilers clang/clang++ from the Xcode command-line tools
Windows native
Visual C++
Windows non-native
MinGW (MSys2, Git Bash)
Cygwin
They are not compatible to each other so you need to stick to one for consistency. In our course, the GNU g++ (part of GCC) is chosen as the only compiler and you must stick to it. Although it is not binary compatible to g++, code that is compilable using LLVM compilers clang++ can be compiled with g++ in most of the case.
Warning
It is not recommended to use any variation of MinGW, such as MSys2, Git Bash to compile your code in this course! It is known to cause a lot of compatibility issues and you must perform a final test on a Linux system (e.g. the SSH server) to make sure that it will work during grading.
Building process of C/C++ projects¶
General compilation process of C/C++ projects contains three main stages: preprocessing, compilation, and linking.
- Preprocessing step¶
Process preprocessing directives like #include, #define, etc. Performs textual manipulations mostly.
- Compilation step¶
Translate the source code (.cpp) files to object (.o) files.
- Linking step¶
Link object (.o) files to produce an executables (or a library)
The three steps of preprocessing, compilation, and linking can be performed either all at once or separately using the g++ compiler. Typically, the preprocessing and compilation steps are performed together. Therefore, the typical step-wise compilation process involves two steps: preprocessing and compilation, followed by linking.
The g++ command¶
g++ is the GNU C++ compiler invocation command. It builds C++ source codes to produce libraries or executables. It serves as pre-processor, compiler, and linker for C++ projects.
g++ command-line¶
A typical g++ compilation command-line looks like:
g++ <options> <arguments>
- g++ options¶
Something started with a hyphen character. They are provided to control the behavior of the compilation. Some options come with their own arguments.
- g++ arguments¶
Everything in the command-line that are not options. They usually specify the source of the compilation. They can be either .cpp file or .o file names.
Example: in g++ -Wall -std=c++14 -o main main.cpp
command, -Wall
,
-std=c++14
and -o main
are options and main.cpp
is an argument.
Common g++ options¶
- -o¶
This option specify the name of the output file. It must be followed by a name. For example,
-o main
to generate a file calledmain
.- -c¶
This option tell g++ to compile only without linking. It will generate an object (.o) file.
- -std=c++14¶
Use the C++ standard 14 to compile. There are other C++ standards available such as C++ 11, C++ 17, C++20, etc.
- -Wall¶
Show common warning messages. Many warning messages are helpful to give you hints on potential problem in your code.
- -g¶
Store debugging information during compilation to allow future use of debuggers like
gdb
orvalgrind
.- -O0, -O1, -O2, -O3¶
Optimize the generated code. The higher the number, the more optimization will be performed. The default is -O2.
Preprocessor directives¶
Preprocessor directives are instructions that are mixed in with C++ statements in your source code. They direct the preprocessor to manipulate the text of the source code files before they are compiled. These directives are identified by the hash symbol ‘#’ at the start of the instruction.
Warning
Preprocessor directives are not statements and no semicolons are needed as the termination of directives. They are line based instead.
#include
It is used to copy texts from another file and paste to the location of the directive. You can use either angular brackets
<>
or double quotation marks""
to include system headers or user headers:// user header #include "mylib.hpp" // system header #include <iostream>
Note
The order of inclusion matters and it is recommended to to start from local headers to system headers. Refer to the best practices documents for more details.
#define
It is used to define macros. Un-parameterized macros without values are preprocessor only flags. Un-parameterized macros with values can be used like a text replacement token or a constant. Parameterized macros work like functions. It is not preferred in the courses! Use functions instead!
// preprocessor only flags #define HEADER_HPP // constant like macro #define PI 3.14159
conditional compilation
#if
,#elif
,#else
,#ifdef
,#ifndef
,#endif
They can be used to direct the preprocessor to include code blocks conditionally. These directives may greatly improve efficiency of the compilation process in large projects in trade of the readability and thus maintainability. In our course, we only use them in header guards.
Header guards
Header guards are the preferred way to prevent header contents from being included more than once. It is preferred over the alternative way using
#progma once
, which is a non-standard C++ directive.#ifndef MYHEADER_HPP #define MYHEADER_HPP // header contents #endif // MYHEADER_HPP
Warning
You must add header guards to all user headers!
Styles of building¶
One step building¶
You may use a single g++ command to produce the executable from the source code files. In this method, all three stages are invoked by one g++ command.
You can list all cpp files in the command line as arguments and specify the output file name as main:
g++ -std=c++14 -o main main.cpp lib1.cpp lib2.cpp
You may also use wildcard to match all cpp files if only one cpp file contains a main function:
g++ -std=c++14 -o main *.cpp
Note
It will fail if you have multiple main functions in your cpp files.
Modular building¶
In the modular building approach, the preprocessing + compilation and linking steps are performed separately to provide fine control over arguments and fine grained error handling.
Use the same example with a main.cpp
, a lib1.cpp
, and a lib2.cpp
with paired headers. You can build your main
executable in steps:
g++ -std=c++14 -Wall -c main.cpp
g++ -std=c++14 -Wall -c lib1.cpp
g++ -std=c++14 -Wall -c lib2.cpp
g++ -o main main.o lib1.o lib2.o
The first three lines compiles all source code files to the corresponding
object files respectively. The last line links all object files into an
executable named main
.
Warning
Modular compilation is always preferred as every step may trigger errors and they are much easier to fix separately It is complicated but can be automated using building tools such as GNU make, etc.
Pitfalls¶
List header files in the g++ command
g++ -o main main.cpp lib1.hpp lib1.cpp
Common Error Messages From g++¶
Note
Some error messages are misleading. Thus, do not stick to it if you cannot figure it out. Try other method such as code review to solve the problem instead.
Alway solve only one error message and re-compile to see the result
Compilation errors
Error happens during compilation of .cpp files to .o files.
undeclared identifier ...
Means that the compiler cannot find a matching declaration for an identifier usage
Misspelled identifier (variable name, type name, class name, etc) when you declare or use an identifier
Forget to include the header that declared the identifier
No matching function ...
Means that the compiler cannot find a matching declaration for a function call
Misspelled function name when you declare or call a function
Forget to include the header that declared the function
Linker errors
Error happens during the linking of .o files to an executable. They usually end with
ld returned 1 exit status
in the last line.undefined reference to ...
Means that the linker cannot find a function definition for a given declaration
misspelled function name when you declare or define a function
Forget to include the .o file where the function definition is
multiple definition of ... first defined here
Means that you have multiple function definitions with a same name and parameter list
You have included a wrong .o file in the g++ command. For example, two .o file each has a main function.
A failed function overloading
A misspelled function name in the definition so it conflict with another function