Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 2f9cc69c authored by NGUYEN-VAN-YEN Benjamin's avatar NGUYEN-VAN-YEN Benjamin
Browse files

Corrections 6 - Real Life C++

parent 1e19b659
No related tags found
No related merge requests found
Pipeline #953419 failed
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [C++ in a real environment](./0-main.ipynb) - [Set up environment](./1-SetUpEnvironment.ipynb)
%% Cell type:markdown id: tags:
## Introduction
I will present here briefly how to set up a minimal development environment... only in Unix-like systems: sorry for Windows developers, but I have never set up a Windows environment for development. You may have a look at WSL, which is gaining traction and enables you to use Linux inside Windows.
This will explain installation for two mainstreams compilers: [GNU compiler for C++](https://en.wikipedia.org/wiki/GNU_Compiler_Collection) and [clang++](https://en.wikipedia.org/wiki/Clang).
## Installing a compiler
**Note:** Compilers themselves will be addressed more in depth in an [upcoming notebook](3-Compilers.ipynb).
### Ubuntu / Debian
%% Cell type:markdown id: tags:
#### Clang
To install `clang` you need to specify explicitly the required version, e.g.:
%% Cell type:code id: tags:
``` C++17
// In a terminal
sudo apt-get install -y clang++-13
```
%% Cell type:markdown id: tags:
#### Default g++
%% Cell type:code id: tags:
``` C++17
// In a terminal
sudo apt-get install -y g++
```
%% Cell type:markdown id: tags:
#### More recent gcc
However, Ubuntu is rather conservative and the version you get might be a bit dated and it might be problematic if you intend to use the bleeding-edged features from the latest C++ standard (even if it's now better than what it used to be).
In February 2024, default Ubuntu provided gcc 12; if you want gcc 13 you need to use the following [PPA](https://launchpad.net/ubuntu/+ppas):
(_disclaimer_: the instructions below have been tested in a Docker image - with `RUN` instead of `sudo` of course - so all the lines might not be necessary in a full-fledged Ubuntu distro)
%% Cell type:code id: tags:
``` C++17
// In a terminal
// Update the Ubuntu environment
sudo apt-get update && sudo apt-get upgrade -y -q && sudo apt-get dist-upgrade -y -q && sudo apt-get -y -q autoclean && sudo apt-get -y -q autoremove)
// To enable `add-apt-repository` command; probably not required in a full-fledged installation
// but you will need it in a Docker image for instance
sudo apt-get install --no-install-recommends -y software-properties-common gpg-agent wget
// Adding PPA and making its content available to `apt`.
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update -y
// Installing the more recent gcc
sudo apt-get install -y g++-13
```
%% Cell type:markdown id: tags:
And to tell the system which version to use, the command is:
%% Cell type:code id: tags:
``` C++17
// In a terminal
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 100
```
%% Cell type:markdown id: tags:
More realistically, you will install gcc and perhaps gfortran as well; the following command make sure all are kept consistent (you do not want to mesh gcc 12 with g++ 13 for instance...):
%% Cell type:code id: tags:
``` C++17
// In a terminal
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100
--slave /usr/bin/g++ g++ /usr/bin/g++-13
--slave /usr/bin/gfortran gfortran /usr/bin/gfortran-13
```
%% Cell type:markdown id: tags:
### Fedora
For development purposes I rather like Fedora, which provides more recent versions than Ubuntu and also a simple clang installation.
%% Cell type:markdown id: tags:
#### g++
%% Cell type:code id: tags:
``` C++17
// In a terminal
sudo dnf install -y gcc gcc-c++ gcc-gfortran
```
%% Cell type:markdown id: tags:
#### clang++
%% Cell type:code id: tags:
``` C++17
// In a terminal
sudo dnf install -y clang
```
%% Cell type:markdown id: tags:
### macOS
* Install XCode from the AppStore
* Install if requested the command line tools
This will install _Apple Clang_ which is a customized version of `clang`. Unfortunately, Apple stopped providing the version of mainstream clang it is built upon; it's therefore more difficult to check if a new standard feature is already supported or not.
It is also possible to install gcc on macOS; I personally use [Homebrew](https://brew.sh) to do so. However using it is not always reliable (on STL side at least - see below), especially shortly after a new version of XCode / Command Line Tools has been published; I would advise to use primarily clang.
%% Cell type:markdown id: tags:
## STL
Besides the compiler, you may also choose which implementation of the STL you want to use. There are two mainstream choices:
- `libstdc++`, which is the STL provided along gcc by GNU. This is the default choice for many Linux distro, and there is fat chance the binaries, libraries and share libraries in your package system were compiled with this one.
- `libstdc++`, which is the STL provided along gcc by GNU. This is the default choice for many Linux distro, and there is a good chance the binaries, libraries and share libraries in your package system were compiled with this one.
- `libc++`, which is the STL provided along clang by LLVM. It is the default choice on macOS, and was until recently a pain to use with Ubuntu (according to Laurent it is much better now in Ubuntu 20.04 and more recent versions).
Both are pretty solid choices:
- Going with `libstdc++` is not a bad idea if you're using with your code libraries from your package manager that uses up this STL implementation (likely in a Linux distro).
- Going with `libstdc++` is not a bad idea if you're using with your code libraries from your package manager that use this STL implementation (likely in a Linux distro).
- Going with `libc++` along with clang++ seems rather natural as well.
%% Cell type:markdown id: tags:
Just a note for compatibility: `libc++` tends to provide more `include` directive in its header files than `libstdc++`. So don't be astonished if your code compiles well with `libc++` but complains about an unknown symbol from STL with `libstdc++` (and the patch is simply to use the missing include - a tool such as IncludeWhatYouUse would have underlined the missing include even when using `libc++`).
%% Cell type:markdown id: tags:
## Editor
%% Cell type:markdown id: tags:
You will need a [source code editor](https://en.wikipedia.org/wiki/Source_code_editor) to write your code; usually your system will provide a very basic one and you may be able to install another on your own.
From my experience, there are essentially two types of developers:
- Those that revel in using [VIM](https://en.wikipedia.org/wiki/Vim_(text_editor)) **or** [emacs](https://en.wikipedia.org/wiki/Emacs), which are lightweight editors that have been around for decades and are rather powerful once you've climbed a (very) steep learning curve.
- Those that will like [integrated development environment](https://en.wikipedia.org/wiki/Integrated_development_environment) more, which provides more easily some facilities (that can often be configured the hard way in the aforementioned venerable text editors) but are more resource-intensive.
I suggest you take whichever you're the most comfortable with and don't bother about the zealots that tell you their way is absolutely the best one.
_Vim_ and _emacs_ are often either already installed or available easily with a distribution package (_apt_, _dnf_, etc...); for IDEs here are few of them:
* [Visual Studio Code](https://code.visualstudio.com/), which gained traction in last few years and is one of the most sizeable GitHub project. This is an open-source and multi-platform editor maintained by Microsoft, not to be confused with [Visual Studio](https://visualstudio.microsoft.com/?rr=https%3A%2F%2Fwww.google.com%2F) - also provided by Microsoft on Windows (and with a fee).
* [CLion](https://www.jetbrains.com/clion/) by JetBrains is also a rising star in IDEs; a free version is available for students (you may know PyCharm by the same company)
* [Eclipse CDT](https://www.eclipse.org/cdt/) and [NetBeans](https://netbeans.org/) are other IDEs with more mileage.
* [QtCreator](https://www.qt.io/qt-features-libraries-apis-tools-and-ide) is not just for Qt edition and might be used as a C++ IDE as well.
* [XCode](https://developer.apple.com/xcode) is the editor provided by Apple on macOS.
* [KDevelop](https://www.kdevelop.org/) is the IDE from the KDE project.
* [JupyterLab](https://jupyter.org/) this very same notebook lab can be used as IDE after the last improvements and extensions added, [see this](https://towardsdatascience.com/jupyter-is-now-a-full-fledged-ide-c99218d33095) and how include the [VS Code Monaco Editor](https://imfing.medium.com/bring-vs-code-to-your-jupyterlab-187e59dd1c1b). I wouldn't advise it but if you're really keen to roll with it.
%% Cell type:markdown id: tags:
## Software configuration manager
A [software configuration manager](https://en.wikipedia.org/wiki/Software_configuration_management), sometimes abbreviated as **SCM**, is important when you're writing code that is meant to stay at least a while (and very handy even if that is not the case).
It is useful not only when you're working with someone else: if at some point you're lost in your code and don't understand why what was working perfectly few hours ago is now utterly broken it is really helpful to be able to compare what has changed since this last commit.
The most obvious choice for a SCM is [git](https://git-scm.com) which is now widely abroad and has become the _de facto_ standard. _git_ is extremely versatile but you can already do a lot of version control with around 10 commands so the learning curve is not as steep as you may fear.
git is generally already installed on your system or readily available through your package manager (or by installing XCode and its tools on macOS).
There are many tutorials about how to use Git; including some from Inria:
- [Support](https://gitlab.inria.fr/aabadie/formation-git) using in the training sessions by Alexandre Abadie (SED Paris).
- [Support](https://gitlab.inria.fr/aabadie/formation-git) used in the training sessions by Alexandre Abadie (SED Paris).
- A [nice tutorial](https://tutorial.gitlabpages.inria.fr/git/) from Mathias Malandain (SED Rennes) to learn to use Git and Gitlab at the same time.
There are also fairly often training sessions organized in the Inria centers; ask your local SED if you're interested by one!
There are also fairly frequent training sessions organized in the Inria centers; ask your local SED if you're interested by one!
%% Cell type:markdown id: tags:
## Build system
Handling properly the compilation of the code is not an easy task: many tutorial skip entirely the topic or just show a very basic example that is very far removed from a real project with potentially many third-party dependencies. This is understandable (and I will mostly do the same): using properly a build system is not trivial and may be the topic on a full lecture of its own.
The usual possibilities are:
* Build system provided by your IDE. Might be easier to use (definitely the case for XCode which I'm familiar with once you grasp how it is intended to work) but you bind your potential users to use the same IDE (even if now some relies upon CMake).
* [Makefile](https://en.wikipedia.org/wiki/Makefile) is the venerable ancestor, which is really too painful to write and not automated enough for my taste.
* [Ninja](https://ninja-build.org) is presented on this website as _a small build system with a focus on speed. It differs from other build systems in two major respects: it is designed to have its input files generated by a higher-level build system, and it is designed to run builds as fast as possible_. It is my favorite generator to use with CMake; meson also enables usage of Ninja under the hood.
* [CMake](https://cmake.org) is the build system probably with the more traction now; it is a cross-platform build system which is rather powerful but not that easy to learn. Official documentation is terse; you may try [this](https://cliutils.gitlab.io/modern-cmake/) or [that](https://cgold.readthedocs.io/en/latest/) to understand it better. Please notice CMake was heavily changed when switching from version2 to version 3; take a recent documentation if you want to learn "modern" CMake. The principle of CMake is to provide a generic configuration that may be used for different build tools: by default you generate a Makefile, but you may choose another generator such as Ninja (see below) or a specific IDE.
* [CMake](https://cmake.org) is the build system probably with the more traction now; it is a cross-platform build system which is rather powerful but not that easy to learn. Official documentation is terse; you may try [this](https://cliutils.gitlab.io/modern-cmake/) or [that](https://cgold.readthedocs.io/en/latest/) to understand it better. Please notice CMake was heavily changed when switching from version2 to version 3; take a recent documentation if you want to learn "modern" CMake. The principle of CMake is to provide a generic configuration that may be used for different build tools: by default you generate a Makefile, but you may choose another generator such as Ninja (see above) or a specific IDE.
* [meson](https://mesonbuild.com/) is a more recent alternative which aims to be simpler to use than CMake. Never used it so can't say much about it.
* [SCons](https://www.scons.org/) is a build system built upon Python which lets you write your own Python functions in the build system. The concept is appealing, but the actual use is actually dreadful and the provided build is much slower than what other build system provides. Avoid it!
We will illustrate in next notebook a basic use of CMake.
**Important:** Nowadays build systems can leverage the powerful computer on which they run and use several processors at the same time. Depending on the tool you use, the default build might be sequential or parallel (on the few I have used, only `ninja` assumes a parallel build by default). Make sure you know how your build tool works and that you're leveraging parallel builds!
%% Cell type:markdown id: tags:
[© Copyright](../COPYRIGHT.md)
......
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [C++ in a real environment](/notebooks/6-InRealEnvironment/0-main.ipynb) - [File structure in a C++ program](/notebooks/6-InRealEnvironment/2-FileStructure.ipynb)
%% Cell type:markdown id: tags:
## Library and program
Contrary to for instance Python or Ruby, C++ is not a scripting language: it is intended to build either an **executable** or **library**.
To summarize:
* An **executable** runs the content of the [`main() function`](../1-ProceduralProgramming/4-Functions.ipynb#A-very-special-function:-main). There should be exactly one such function in all the compiled files; the file with this `main` must be compiled.
* A **library** is a collection of functions, classes and so on that might be used in a program. A library may be **header-only**: in this case it is just an ensemble of header files with no file compiled. In this case all the definitions must be either **inline** or **template** (and possibly both of course).
### Static and shared libraries
A (non header) library may be constructed as one of the following type:
* A **static** library, usually with a **.a** extension, is actually included directly into any executable that requires it. The advantage is that you just need the bare executable to run your code: the library is no longer required at runtime. The inconvenient is that the storage space may balloon up rather quickly: each executable will contain the whole library!
* A **shared** library, which extension may vary wildly from one OS to another (**.dylib**, **.so**, **.dll**, etc...), is on the other hand required at runtime by the executable that was built with it. The advantage is that executables are thus much smaller. They are often described on the Web as the way to go; my personal experience with them is however less rosy as each OS handles them differently (noticeably the way to indicate in which location the dynamic libraries should be looked at differ rather wildly...)
The best if possible is to enable generation of your library in either type... but it requires a bit of work with your build system.
## Source file
Contrary to most of more modern languages, C++ relies upon two very specific kind of files, each of which with their own extension schemes. We will introduce first the source file, with which basic programs might be achieved, and then show why header files are also needed.
### Compilation of _Hello world!_
A source file is a type of file intended to be **compiled**.
Let's consider the seminal _Hello world_ in a dedicated source file named _hello.cpp_ (all the examples here are made available in `2c-Demo` directory; this one is `01-HelloWorld`):
%% Cell type:code id: tags:
``` C++17
// File hello.cpp - I put "Code" as cell type in Jupyter to get nice colors but it's not intended
// to be executed in the cell!
#include <iostream>
int main(int argc, char** argv)
{
std::cout << "Hello world!" << std::endl;
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
To compile it on a Unix system, you will need to type in your terminal a line that looks like (with at least [GNU compiler for C++](https://en.wikipedia.org/wiki/GNU_Compiler_Collection) and [clang++](https://en.wikipedia.org/wiki/Clang)):
%% Cell type:code id: tags:
``` C++17
// In a terminal
g++ -std=c++17 hello.cpp -o hello
```
%% Cell type:markdown id: tags:
where:
- `g++` is the name of the compiler. You may provide `clang++` if you wish.
- `-std=c++17` tells to use this version of the standard. If not specified the compilers tend to assume C++ 11 but may issue warnings if some features introduced with this standard are used.
- `hello.cpp` is the name of the source file.
- `hello` is the name of the executable produced. If the `-o hello` is omitted, the executable is arbitrarily named `a.out`, exactly as in C.
%% Cell type:markdown id: tags:
The executable may then be used with:
%% Cell type:code id: tags:
``` C++17
// In a terminal
./hello
```
%% Cell type:markdown id: tags:
The `./` is there to specify the executable is to be looked at in current path; it may be omitted if `.` is present in the system `PATH` environment variable.
Please notice the name of the file with the `main()` function and the name of the executable are completely custom; you have no requirement on the names of files and executable.
%% Cell type:markdown id: tags:
If your current machine has the compilers installed it is possible to execute these compilation commands instead of opening the terminal use the ! symbol as follows:
%% Cell type:code id: tags:
``` C++17
!g++ -std=c++17 ./2c-Demo/01-HelloWorld/hello.cpp -o hello
```
%% Cell type:code id: tags:
``` C++17
!./hello
```
%% Cell type:markdown id: tags:
### Source files extensions
The plural is not a mistake: unfortunately, contrary to many languages, there is no universal convention upon the extensions to use for C++ files. There are widely spread conventions, but a library may choose not to follow them.
Editors and IDE know the most common ones and usually provide a way to add your own spin so that they may provide language recognition and all that goes with it (colored syntax, completion helper and so on).
The most common extensions are **.cpp**, **.cc**, **.C** and more seldom **.cxx**.
My advice would be to choose one and stick to it; the only one I warn against is **.C** because some operating systems (such as macOS) are case-insensitive by default and **.c** is a more common convention for C programs.
%% Cell type:markdown id: tags:
### Expanding our hello program with two source files: one for main, one for the function
This code is not very subtle: everything is in the same file, so we are in a very simplistic case in which only one file is compiled, and there are no need to find ways to specify how several files relate to each other.
You may imagine working in a single file is not an very common option: it hinders reusability, and it would be cumbersome to navigate in a file with thousands or more lines or code (if you're really curious to an extreme case have a look at the amalgamation ([2.28 Mo zip here](https://www.sqlite.org/2020/sqlite-amalgamation-3310100.zip)) of sqlite code, in which all the code is put in a same source file...)
You may imagine working with a single file is not a very common option: it hinders reusability, and it would be cumbersome to navigate in a file with thousands or more lines of code (if you're curious about an extreme case, have a look at the amalgamation ([2.28 Mo zip here](https://www.sqlite.org/2020/sqlite-amalgamation-3310100.zip)) of sqlite code, in which all the code is put in a same source file...)
We want know to separate the main() and the actual content of the code (also in `2c-Demo/02-InTwoFilesWithoutHeader`):
We now want to separate the main() and the actual content of the code (also in `2c-Demo/02-InTwoFilesWithoutHeader`):
%% Cell type:code id: tags:
``` C++17
// File hello.cpp - no main inside
#include <iostream>
void hello()
{
std::cout << "Hello world!" << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
// File main.cpp
#include <cstdlib> // for EXIT_SUCCESS
int main(int argc, char** argv)
{
hello();
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
This brute force method is not working: a line on a terminal like:
%% Cell type:code id: tags:
``` C++17
// In a terminal
clang++ -std=c++17 hello.cpp main.cpp -o hello
```
%% Cell type:markdown id: tags:
would yield something like:
```verbatim
main.cpp:5:5: error: use of undeclared identifier 'hello'
hello();
^
1 error generated.
```
%% Cell type:markdown id: tags:
## Header file
The issue above is that we need to inform the compiler when it attempts to compile `main.cpp` that `hello()` function is something that exists. We need to **declare** it in a dedicated **header file** and **include** this file in each source file that needs it (also in `2c-Demo/03-InTwoFilesWithHeader`):
%% Cell type:code id: tags:
``` C++17
// File hello.hpp
void hello();
```
%% Cell type:code id: tags:
``` C++17
// File main.cpp
#include <cstdlib> // for EXIT_SUCCESS
#include "hello.hpp"
int main(int argc, char** argv)
{
hello();
return EXIT_SUCCESS;
}
```
%% Cell type:code id: tags:
``` C++17
// File hello.cpp - no main inside
#include <iostream>
#include "hello.hpp"
void hello()
{
std::cout << "Hello world!" << std::endl;
}
```
%% Cell type:markdown id: tags:
With this few changes, the command line:
%% Cell type:code id: tags:
``` C++17
// In a terminal
clang++ -std=c++17 hello.cpp main.cpp -o hello
```
%% Cell type:markdown id: tags:
works as expected and creates a valid `hello` executable (also note the header file is not required explicitly in this build command).
As in the previous case we may directly compile from here using the ! symbol as follows (if compilers are present in the environment):
%% Cell type:code id: tags:
``` C++17
!g++ -std=c++17 2c-Demo/03-InTwoFilesWithHeader/hello.cpp 2c-Demo/03-InTwoFilesWithHeader/main.cpp -o hello
```
%% Cell type:code id: tags:
``` C++17
!./hello
```
%% Cell type:markdown id: tags:
### Header location
You may have noticed that in the previous call to compile the executable the header file wasn't provided explicitly.
`hello.hpp` was found because it was in the current folder. Let's suppose now we want to put include files in a directory named `incl`; to make it work we have actually two ways:
* Either modifying the path in the source file. We would get
```c++
#include "incl/hello.hpp"
```
in both `hello.cpp` and `main.cpp`.
* Or by giving to the command line the `-I` instruction to indicate which path to look for (`2c-Demo/04-SpecifyHeaderDirectory`):
%% Cell type:code id: tags:
``` C++17
// In a terminal
clang++ -std=c++17 -Iincl hello.cpp main.cpp -o hello
```
%% Cell type:markdown id: tags:
As many `-I` as you wish may be provided on the command line; I would recommend not providing too many as it increases the risk of an ambiguity if two header files at different path are named likewise:
```verbatim
incl/foo.hpp
bar/incl/foo.hpp
```
and
```shell
clang++ -Iincl -Ibar/incl main.cpp
```
leads to an ambiguity if there is `#include "foo.hpp"` in the `main.cpp`...
%% Cell type:markdown id: tags:
### `""` or `<>`?
You may have noticed I sometimes used `<>` and sometimes `""` to specify the path for the include.
The details don't matter that much in most cases, but it is better to:
* Use `<>` only for the system libraries, typically STL or C headers should be this form.
* Use `""` for your headers or for third-party libraries installed in specific locations.
If you want a bit more details:
* `""` will look first in the current directory, and then in the header files directories.
* `<>` will look only in the header files directories.
%% Cell type:markdown id: tags:
### Header guards and #pragma once
During compilation, the `#include` command is actually replaced by the content of the file which path is provided here. We therefore may quickly include twice the same content (`2c-Demo/05-NoHeaderGuards`):
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
class Foo
{ };
```
%% Cell type:code id: tags:
``` C++17
// File main.cpp
#include <cstdlib>
#include "foo.hpp"
#include "foo.hpp" // Oops...
int main()
{
return EXIT_SUCCESS;
}
```
%% Cell type:code id: tags:
``` C++17
// In terminal
clang++ -std=c++17 main.cpp -o does_not_compile
```
%% Cell type:markdown id: tags:
doesn't compile: the translation unit provides two declarations of class Foo!
This might seem a simple enough mistake to fix it, but in a project with few header files that might be intricated it becomes quickly too much a hassle (`2c-Demo/06-MoreSubtleNoHeaderGuards`):
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
class Foo
{ };
```
%% Cell type:code id: tags:
``` C++17
// File bar.hpp
#include "foo.hpp"
struct Bar
{
Foo foo_;
};
```
%% Cell type:code id: tags:
``` C++17
// File main.cpp
#include <cstdlib>
#include "foo.hpp"
#include "bar.hpp" // Compilation error: "foo.hpp" is sneakily included here as well!
int main()
{
Bar bar;
return EXIT_SUCCESS;
}
```
%% Cell type:code id: tags:
``` C++17
// In terminal
clang++ -std=c++17 main.cpp -o does_not_compile
```
%% Cell type:markdown id: tags:
The patch is to indicate in each header file that it should be included **only once**.
#### #pragma once
There is the easy but non standard approach that is nonetheless [widely supported](https://en.wikipedia.org/wiki/Pragma_once#Portability) by compilers (`2c-Demo/07-PragmaOnce`):
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
#pragma once
class Foo
{ };
```
%% Cell type:code id: tags:
``` C++17
// File bar.hpp
#pragma once
#include "foo.hpp"
struct Bar
{
Foo foo_;
};
```
%% Cell type:code id: tags:
``` C++17
// File main.cpp
#include <cstdlib>
#include "foo.hpp"
#include "bar.hpp"
int main()
{
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
This prevents the inclusion of `foo.hpp` twice; and now `clang++ -std=c++17 main.cpp -o do_nothing` compiles correctly.
%% Cell type:markdown id: tags:
#### Header guards
The "official" way to protect files - the use of so-called **header guards** - fully supported by the standard, is much more clunky (`2c-Demo/08-HeaderGuards`):
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
#ifndef FOO_HPP // If this macro is not yet defined, proceed to the rest of the file.
#define FOO_HPP // Immediately define it so next call won't include again the file content.
class Foo
{ };
#endif // FOO_HPP // End of the macro block that begun with #ifndef
```
%% Cell type:code id: tags:
``` C++17
// File bar.hpp
#ifndef BAR_HPP // If this macro is not yet defined, proceed to the rest of the file.
#define BAR_HPP // Immediately define it so next call won't include again the file content.
#include "foo.hpp"
struct Bar
{
Foo foo_;
};
#endif // BAR_HPP // End of the macro block that begun with #ifndef
```
%% Cell type:markdown id: tags:
You may check that `clang++ -std=c++17 main.cpp -o do_nothing` compiles properly as well.
%% Cell type:markdown id: tags:
##### **[WARNING]** Ensure unicity of header guards
There is however a catch with header guards: you must ensure that the macro for a given file is used only once. Let's consider the previous case, but with a bug (`2c-Demo/09-HeaderGuardsBug`):
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
#ifndef FOO_HPP // If this macro is not yet defined, proceed to the rest of the file.
#define FOO_HPP // Immediately define it so next call won't include again the file content.
class Foo
{ };
#endif // FOO_HPP // End of the macro block that begun with #ifndef
```
%% Cell type:code id: tags:
``` C++17
// File bar.hpp
#ifndef FOO_HPP // bug here!
#define FOO_HPP
#include "foo.hpp"
struct Bar
{
Foo foo_;
};
#endif // FOO_HPP
```
%% Cell type:markdown id: tags:
`clang++ -std=c++17 main.cpp` does not compile, with the terse message:
```shell
main.cpp:7:5: error: unknown type name 'Bar'
Bar bar;
```
%% Cell type:markdown id: tags:
And in a more developed code, it might be a nightmare to identify this kind of bug...
A common strategy is to define a header guard name based on the location of the source file in the tree; this circumvent the case in which two files share a same name (quite common in a large codebase...)
One of us (Sébastien) uses up a [Python script](https://gitlab.inria.fr/MoReFEM/CoreLibrary/MoReFEM/raw/master/Scripts/header_guards.py) which iterates through all the C++ files in his library, identify the header guards of each header file and check they are a mix of the project name and the path of the file. This is definitely much more clunky than **#pragma once** !
But as we said the latter is non standard and there are hot discussions about whether it is safe or not for all set-ups (at some point it was complicated to use if there were symbolic or hard links in the project).
%% Cell type:markdown id: tags:
### Header files extensions
The most current header files extensions are **.hpp**, **.h**, **.hh** and more seldom **.hxx**. I definitely not recommend **.h**: this is also the extension used for C header files, and some compiler even issue a warning if you're using it in a C++ context.
The most current header files extensions are **.hpp**, **.h**, **.hh** and more seldom **.hxx**. I definitely do not recommend **.h**: this is also the extension used for C header files, and some compiler even issue a warning if you're using it in a C++ context.
#### My personal convention (Sébastien)
Personally I am using both **.hpp** and **.hxx**:
* **.hpp** is for the declaration of functions, classes, and so on.
* **.hxx** is for the definitions of inline functions and templates.
The **.hxx** is included at the end of **.hpp** file; this way:
* End-user just includes the **.hpp** files in his code; he **never** needs to bother about including **.hxx** or not.
* The **hpp** file is not too long and includes only declarations with additionally Doxygen comments to explain the API.
And you may have noticed that standard library headers get no extension at all!
%% Cell type:markdown id: tags:
## Why a build system: very basic CMake demonstration
Let's take back our mighty "Hello world" example with a slight extension: we want to query the identity of the user and print that instead. We will foolishly add this new function in yet another file for the sake of illustration only (`2c-Demo/10-CMake`):
%% Cell type:code id: tags:
``` C++17
// File hello.hpp
#ifndef HELLO_HPP
#define HELLO_HPP
void Hello();
#endif // HELLO_HPP
```
%% Cell type:code id: tags:
``` C++17
// File who-are-you.hpp
#ifndef WHO_ARE_YOU_H
#define WHO_ARE_YOU_H
#include <string>
std::string WhoAreYou();
#endif // WHO_ARE_YOU_H
```
%% Cell type:code id: tags:
``` C++17
// File hello.cpp
#include <iostream>
#include "hello.hpp"
#include "who-are-you.hpp"
void hello()
{
auto identity = WhoAreYou();
std::cout << "Hello " << identity << '!' << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
// File who-are-you.cpp
#include <iostream>
#include "who-are-you.hpp"
std::string WhoAreYou()
{
std::string name;
std::cout << "What's your name? ";
std::cin >> name; // not much safety here but this is not the current point!
return name;
}
```
%% Cell type:code id: tags:
``` C++17
// File main.cpp
#include <cstdlib> // For EXIT_SUCCESS
#include "hello.hpp"
int main(int argc, char** argv)
{
Hello();
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
Up to now, we compiled such a program with manually:
%% Cell type:code id: tags:
``` C++17
// In terminal
clang++ -std=c++17 -c hello.cpp
clang++ -std=c++17 -c main.cpp
clang++ -std=c++17 -c who-are-you.cpp
clang++ -std=c++17 *.o -o hello
```
%% Cell type:markdown id: tags:
The issue with that is that it's not robust at all: either you recompile everything all the time (and let's face it: it's tedious even with our limited number of files...) or you have to keep track of which should be recompiled. For instance if `who-are-you.hpp` is modified all source files include it and must be recompiled, but if it is `hello.hpp` `who_are_you.cpp` is not modified.
It is to handle automatically this and limit the compilation to only what is required that build systems (which we talked about briefly [here](./1-SetUpEnvironment.ipynb#Build-system)) were introduced. Let's see a brief CMake configuration file named by convention `CMakeLists.txt`:
%% Cell type:code id: tags:
``` C++17
# CMakeLists.txt
# Ensure the cmake used is compatible with the CMake functions that are used
cmake_minimum_required(VERSION 3.20)
# A project name is mandatory, preferably right after cmake_minimum_required call
project(Hello)
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard; at least 17 is expected.")
add_executable(hello
main.cpp
hello.cpp
who-are-you.cpp)
```
%% Cell type:code id: tags:
``` C++17
// In terminal
mkdir build // create a directory to separate build from source files and so on
cd build
cmake .. // will create the Makefile; as no generator was provided with -G Unix makefile is chosen.
// The directory indicated by .. MUST include the main CMakeLists.txt of the project.
make
```
%% Cell type:markdown id: tags:
This command creates the executable in current directory; now if we modified one file the build system will rebuild all that needs it and nothing more.
%% Cell type:markdown id: tags:
If `main.cpp` and `hello.cpp` may also be used jointly for another executable, they may be put together in a library; replace the former `add_executable` command by:
%% Cell type:code id: tags:
``` C++17
add_library(hello_lib
SHARED
hello.cpp
who-are-you.cpp)
add_executable(hello
main.cpp)
target_link_libraries(hello
hello_lib)
```
%% Cell type:markdown id: tags:
SHARED may be replaced by STATIC to use a static library instead.
%% Cell type:markdown id: tags:
You can run these commands directly with the ! symbol as follows:
%% Cell type:code id: tags:
``` C++17
!cd ./2c-Demo/7-CMake/ && mkdir build && cd build && cmake .. && make
```
%% Cell type:code id: tags:
``` C++17
!cd ./2c-Demo/7-CMake/build && ./hello
```
%% Cell type:markdown id: tags:
## Where should the headers be included?
* Each time a header is modified, all the source files that include it directly or indirectly are recompiled.
* Each time a source file is modified, only this source file is modified; some relinking for the libraries and executables that depend on it will also occur (linking is the step that glue together the object files and libraries; the term _compilation_ is often - included in this very tutorial - abusively used to encompass both compilation and link phases).
Thus it might seem a good idea to put as much as possible `#include` directives in the source files **rather than in include files**... hence limiting the compilation time. This is a generally very good advice... provided we do not err on the wrong side and put enough in the header file:
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
#ifndef FOO_HPP
# define FOO_HPP
#include <string>
void Print(std::string text);
#endif // FOO_HPP
```
%% Cell type:code id: tags:
``` C++17
// File foo.cpp
#include <iostream>
#include "foo.hpp"
void Print(std::string text)
{
std::cout << "The text to be printed is: \"" << text << "\"." << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
// File main.cpp
#include <cstdlib>
#include "foo.hpp"
int main()
{
Print("Hello world!");
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
You may have noticed `string` and `iostream` are not dealt with the same way... and rightly so:
* `#include <iostream>` is only in the source file: it is actually needed only for `std::cout` and `std::endl`, which are implementation details of `Print()` function: neither appears in the signature of the function.
* `#include <string>` is present in `foo.hpp` as it is required to give the information about the type of the prototype to be used. If you do not do that, each time you include `foo.hpp` you would need to include as well `string`; doing so leads to unmaintainable code as you would have to track down all the includes that are required with each include...
So to put in a nutshell:
* Put in the header files all the includes that are mandatory to make the prototypes understandable. A rule of thumb is that a source file that would only include the header file should be compilable:
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
std::string Print();
```
%% Cell type:code id: tags:
``` C++17
// File check_foo.cpp
#include <cstdlib>
#include "foo.hpp"
int main(int, char**)
{
return EXIT_SUCCESS;
} // DOES NOT COMPILE => header is ill-formed!
```
%% Cell type:markdown id: tags:
* Include that are here for implementation details should on the other hand be preferably in source files. Of course, you may not be able to do that in any case: for instance templates are by construction defined in header files!
%% Cell type:markdown id: tags:
Some tools such as [include-what-you-use](https://include-what-you-use.org/) are rather helpful to help cut off the unrequired includes in file, but they need a bit of time to configure and set up properly, especially on an already large codebase.
%% Cell type:markdown id: tags:
## Forward declaration
There is actually an exception to the first rule I've just given: **forward declaration**. This is really a trick that may be used to reduce compilation time, with some caveats.
The idea is that if a type intervenes in a header file **only as a reference and/or as a (smart) pointer**, it might be forward-declared: its type is merely given in the header (`2c-Demo/11-Forward`)
%% Cell type:code id: tags:
``` C++17
// File foo.hpp
#ifndef FOO_HPP
# define FOO_HPP
// Forward declaration: we say a class Bar is meant to exist...
class Bar;
struct Foo
{
Foo(int n);
void Print() const;
Bar* bar_ = nullptr;
};
#endif // FOO_HPP
```
%% Cell type:code id: tags:
``` C++17
// File check_header_ok.cpp
#include <cstdlib>
#include "foo.hpp"
int main(int, char**)
{
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
and `clang++ -std=c++17 check_header_ok.cpp` compiles properly (you may try commenting out the forward declaration line to check it does not without it)
This is not without cost: obviously in a file where `Bar` is actually needed you will need to include it properly: with just `#include "foo.hpp"` you can't for instance call a method of `Bar` class.
Typically the `include "bar.hpp"` will be located in the `foo.cpp` file, in which you will probably need the `Bar` object interface to define your `Foo` object (or if not you may question why you chose to put the `bar_` data attribute in the first place)
It is nonetheless a very nice trick to know; there is even an idiom call [Pimpl idiom](https://arne-mertz.de/2019/01/the-pimpl-idiom/) that relies upon forward declaration.
This is however not the only use for it though: to define a shared_ptr/weak_ptr you [also need](../7-Appendix/WeakPtr.ipynb) to use this capability.
The tool [include-what-you-use](https://include-what-you-use.org/) mentioned earlier is able to suggest as well what should be forward-declared.
%% Cell type:markdown id: tags:
[© Copyright](../COPYRIGHT.md)
......
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [C++ in a real environment](./0-main.ipynb) - [Clang ang gcc compilers](./3-Compilers.ipynb)
%% Cell type:markdown id: tags:
## Introduction
I will present here briefly some characteristics of both gcc and clang compilers.
I recommend using them both (and more if you can!): each compiler gets its own spin on the standard, and sometimes a perfectly valid code will be refused by one... whereas (more often) invalid code will unduly get a free pass with one of the compilers.
So the more compilers with which you may test your code, the merrier!
If you're Inria staff, Inria also provides a licence to [Intel compiler](https://software.intel.com/en-us/c-compilers).
## GNU compiler
[GCC](http://gcc.gnu.org/) is a free-to-use compiler which has now been around for decades; it is mostly for Unix systems but may be used with Windows with some additional set-up (I don't master this but see for instance this [StackOverflow question](https://stackoverflow.com/questions/771756/what-is-the-difference-between-cygwin-and-mingw)).
As many others software, GCC changed its version system: gcc 3 and 4 were there for decades, and now the versions change more swiftly, with gcc 13.2 the current stable version (as of February 2024; it was published on the 27th of July 2023).
`gcc` was long known for its terse user interface: until recently color were not provided in outputs, and error messages were a bit cryptic for the beotians. It changed though when `clang` appeared and it is now much more user-friendly.
### Debug and release flags
#### Debug mode
As a reminder, debug mode is the one you should use to develop: it is intended to compile code as fast as possible, and doesn't spend time performing some optimizations. Typical debug flags are:
`-O0 -g`
where:
* `-O0` means no optimization is applied. It might be skipped: it is the default behaviour.
* `-g` means symbols are kept for debug purposes, enabling a debugger to do its bidding.
#### Release mode
On release mode, the goal is more to provide an efficient code, at the cost of higher compilation time. Typical flags are:
`-O3 -DNDEBUG`
where:
* `-DNDEBUG` means the macro `NDEBUG` is defined; this deactivates all asserts in the code.
* `-O3` means as many optimizations as possible should be applied.
You may sometimes find on the Web advocates of `-O2` flag, which performs slightly less optimization than `-O3`, on the ground that `-O3` breaks some code. It was true many years ago... but now if your code breaks under `-O3` it probably means it's buggy, not that optimization is! You may read [this thread](https://stackoverflow.com/questions/11546075/is-optimisation-level-o3-dangerous-in-g) for more about the question; I raised this point because you might be surprised by the number of libraries which still use up `-O2` in their release mode.
You may sometimes find on the Web advocates of `-O2` flag, which performs slightly less optimization than `-O3`, on the ground that `-O3` breaks some code. It was true many years ago... but now if your code breaks under `-O3` it probably means it's buggy, not that optimization is! You may read [this thread](https://stackoverflow.com/questions/11546075/is-optimisation-level-o3-dangerous-in-g) for more about the question; I raised this point because you might be surprised by the number of libraries which still use `-O2` in their release mode.
### Warnings
To my mind, `gcc` is a bit of a mess on the warning side...
There is the flag:
`-Wall`... which activates _some_ warnings, and not all as its name suggests.
You may add:
`-Wextra`... which add some others.
So how to activate them all? No reliable way; my current batch of gcc warnings is (brace yourself!):
`-Wall -Wextra -Wcast-align -Wcast-qual -Wconversion -Wdisabled-optimization -Wfloat-equal -Wformat=2 -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wimport -Winit-self -Winvalid-pch -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wpointer-arith -Wredundant-decls -Wstack-protector -Wstrict-aliasing=2 -Wswitch-enum -Wunreachable-code -Wunused -Wunused-parameter -Wvariadic-macros -Wwrite-strings`
Some are intentionally deactivated, and plenty others I probably don't know - especially if they were introduced since I established this list (by adapting one provided on StackOverflow...) some years ago...
An answer to this [StackOverflow post](https://stackoverflow.com/questions/11714827/how-can-i-turn-on-literally-all-of-gccs-warnings) provides a command to extract the list of all warnings used by gcc (unfortunately without filtering for the language used):
```shell
gcc -Q --help=warning | sed -e 's/^\s*\(\-\S*\)\s*\[\w*\]/\1 /gp;d' | tr -d '\n'
```
### Standard library
GNU also provides its implementation of the standard C++ library, which is called **libstdc++**.
### Fortran support
GNU compiler suite also provides a Fortran compiler, which is often required for many mathematical libraries that use part of this language in their implementation.
## clang
[clang](http://clang.llvm.org/) is a much more recent project (2007) that proposes an interface mostly similar to the one provided by gcc.
This is the compiler I recommend for your development: interface is much more user friendly - even if gcc took note and made progresses on that front. But clang gets for itself:
This is the compiler I recommend for your development: interface is much more user friendly - even if gcc took note and made progress on that front. But clang gets for itself:
* Default syntax coloring of the output.
* More helpful compilation error: there is even an arrow to indicate where in a line the syntax is problematic.
* Faster compilation in debug mode.
### Debug and release mode
See gcc: they took the same.
### Warnings
clang provides many warnings that are the same as gcc... but:
* Some do not behave exactly the same way. For instance I activate `-Wshadow` in clang but not in gcc where it is too cumbersome for my taste.
* Some are specific to a compiler... and it becomes truer with each new version of clang, which often introduces a new warning.
But what I really like with clang is they took the opposite approach warning side: they provide a `-Weverything` which really activates all of them! (`-Wall -Wextra` are also supported but similar to their gcc counterparts).
Of course, you may deactivate a warning you do not want by adding -Wno-foo where _foo_ is the warning not to consider.
In my code, compilation warnings I use are:
```
-Weverything
-Wno-c++98-compat // I assume my code is not C++ 03 compatible
-Wno-c++98-compat-pedantic // same for pedantic warnings
-Wno-padded // I don't want to add char data attributes to make the static size of a class a multiple of 4
-Wno-exit-time-destructors // I use an advanced pattern that doesn't mesh with this one.
-Wno-global-constructors // same
-Wno-documentation // Some Doxygen were incorrectly indicated as inadequate
-Wno-documentation-unknown-command // A valid Doxygen command I used was not recognized
-Wno-undefined-func-template // Requires weird code in header file; didn't understand this one to be honest
-Wno-c++1z-extensions // I assume my code uses up C++ 20
-Wno-c++1z-extensions // I assume my code uses C++ 20
```
What's neat is that your build remains up-to-date with `-Weverything`: if a new warning is added you will possibly see it if your code is affected and then decide if you want to keep it or not.
### Standard library
A new implementation of the standard library is also delivered with clang; it is named **libc++**. I advise you to use it rather than libstdc++ with clang; however it is easier said than done on Ubuntu (at least the last time I tried).
To ensure that, add in your command line:
```
-stdlib=libc++
```
Your build system may already take care of this automatically.
### Fortran support
For a long time, there was no Fortran compiler with LLVM or clang; you therefore had to use something as gfortran if a third-party library you use require it - usually the most recent you may find.
As of 2022, there is something called Flang that exists, but my attempt to use it didn't go far as they chose contrary to clang did years ago to use their own options without pseudo backward compatibility with gfortran's ones. As a result, compilation of third party libraries is tricky as they often assume interface provided by gfortran (Openblas and PETSc for instance won't compile with Flang). So for the time being the best is probably to stick with gfortran if as myself you aren't a Fortran developer but may need it for your third party dependencies.
### Apple Clang
As a side note: macOS provides for few years now a customized clang with its developer environment. This one is not the standard clang and is usually slightly older than the bleeding-edge clang you may find on LLVM site. The drawback is that they stopped indicating the base version upon which their version is built; so it's not that easy to find on the Web whether a feature is supported or not.
%% Cell type:markdown id: tags:
## State of compiler support
There is currently a new C++ standard every three years, but there is a very noticeable lag for all features to be supported by both the compilers and their associated standard library (some C++ 20 features such as `std::format` library still aren't supported in February 2024).
You may check [here](https://en.cppreference.com/w/cpp/compiler_support) what is the state of support for any given features; each compiler also gets its own page but this one has the merit of providing information for many compilers at once.
%% Cell type:markdown id: tags:
## c++filt
We won't delve at all into the details, but just know that to allow stuff such as function overload, namespaces (that we'll cover [shortly](../5-Namespace.ipynb)) and so on, C++ proceed to something called _mangling_ (see [here](http://web.mit.edu/tibbetts/Public/inside-c/www/mangling.html) for instance if you're curious about this process). That's the reason in linker error messages you may see stuff such as `_ZN9cdnalizer11rewriteHTMLINS_6apache8IteratorEcEET_RKSsRKNS_6ConfigES3_S3_St8functionIFS3_RKS3_SB_EES9_IFvSsEE`
We won't delve at all into the details, but just know that to allow stuff such as function overload, namespaces (that we'll cover [shortly](../5-Namespace.ipynb)) and so on, C++ does something called _mangling_ (see [here](http://web.mit.edu/tibbetts/Public/inside-c/www/mangling.html) for instance if you're curious about this process). That's the reason in linker error messages you may see stuff such as `_ZN9cdnalizer11rewriteHTMLINS_6apache8IteratorEcEET_RKSsRKNS_6ConfigES3_S3_St8functionIFS3_RKS3_SB_EES9_IFvSsEE`
The least we can say is that's it's not very user friendly; sometimes what is behind the symbol will be clear enough, sometimes not that so....
The least we can say is that's it's not very user friendly; sometimes what is behind the symbol will be clear enough, sometimes not that much....
Luckily, both clang and gcc are shipped with a nifty tool named `c++filt` to decipher it, with same name and interface:
Luckily, both clang and gcc ship with a nifty tool named `c++filt` to decipher it, with same name and interface:
%% Cell type:markdown id: tags:
```shell
c++filt -n _ZN9cdnalizer11rewriteHTMLINS_6apache8IteratorEcEET_RKSsRKNS_6ConfigES3_S3_St8functionIFS3_RKS3_SB_EES9_IFvSsEE
```
%% Cell type:markdown id: tags:
which returns the signature for humans:
%% Cell type:markdown id: tags:
```c++
cdnalizer::apache::Iterator cdnalizer::rewriteHTML<cdnalizer::apache::Iterator, char>(std::string const&, cdnalizer::Config const&, cdnalizer::apache::Iterator, cdnalizer::apache::Iterator, std::function<cdnalizer::apache::Iterator (cdnalizer::apache::Iterator const&, cdnalizer::apache::Iterator const&)>, std::function<void (std::string)>)
```
%% Cell type:markdown id: tags:
## Wandbox
If you need to know whether a small code is supported by a specific version of clang or gcc, you may use the online compiler facility [Wandbox](https://wandbox.org/) which provides many gcc and clang versions (and even Boost library for good measure).
If you need to know whether some small code is supported by a specific version of clang or gcc, you may use the online compiler facility [Wandbox](https://wandbox.org/) which provides many gcc and clang versions (and even Boost library for good measure).
%% Cell type:markdown id: tags:
[© Copyright](../COPYRIGHT.md)
......
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [C++ in a real environment](/notebooks/6-InRealEnvironment/0-main.ipynb) - [How to include properly third-party libraries](/notebooks/6-InRealEnvironment/4-ThirdParty.ipynb)
%% Cell type:markdown id: tags:
## Introduction
Any sizable code doesn't exist in a void: it's likely it needs to be build upon pre-existing libraries at some point.
Any sizable code doesn't exist in a void: it's likely it needs to be built upon some pre-existing libraries.
We discussed a lot about the STL up-to-now in this tutorial, but it's likely you will need some other libraries more specific to your field (for instance if you're doing numerical computation you'll probably need a solver from a library).
We have mostly talked about the STL up to this point in this tutorial, but it's likely you will need some other libraries more specific to your field (for instance if you're doing numerical computation you'll probably need a solver from a library).
C++ may be interfaced rather easily with C and Fortran code; however a recurrent issue that is the reason of the current notebook is warnings: even highly recommended and used libraries such as [Boost](https://www.boost.org/) are cluttered with warnings and there are no easy solution:
C++ may be interfaced rather easily with C and Fortran code; however a recurrent issue that is the reason of the current notebook is warnings: even highly recommended and used libraries such as [Boost](https://www.boost.org/) are cluttered with warnings and there is no easy solution:
* Fixing the warnings from the third-party library yourself is a dead-end: you would have to redo-it at each upgrade of the library... and doing it in the first place may not be that easy with a base code you do not know.
* Of course, you may ask the library developers to fix it, or even better provide a **pull request** to do it for them... but they may not be keen to accept it, and argue they know what they're doing and that you should shut the damn compiler warning up. But you really shouldn't: the warnings you want to use or not should be dictated by your own needs, not by the third-party libraries.
* Keeping the warnings is also bad: as already mentioned if you're letting even 10 warnings in your code you may not see the dangerous 11th a change in the code may produce.
* Of course, you may ask the library developers to fix it, or even better provide a **pull request** to do it for them... but they may not be keen to accept it, and argue they know what they're doing and that you should silence the damn compiler warnings. But you really shouldn't: the warnings you want to use or not should be dictated by your own needs, not by third-party libraries'.
* Keeping the warnings is also bad: as already mentioned if you're letting even 10 warnings in your code you may not see the dangerous 11th warning that a change in the code may produce.
## Demo
We will follow here the instructions detailed in a dedicated [README](./4b-Demo/README.md) (in 4b-Demo directory).
The remaining of current notebook is a shortcut if you do not want to run the demo; if you do you may stop reading here and switch to the demo.
## Example
As we shall see, there are two very different mechanisms that are present to work around the issue.
To illustrate them we will tackle a very basic example: a program which calls Boost filesystem library to copy a file. We also purposely add code below that should raise a warning:
%% Cell type:code id: tags:
``` C++17
// Does not work as a Jupyter cell: no Boost installed!
#include <cstdlib>
#include <iostream>
#include "boost/exception/diagnostic_information.hpp"
#include "boost/filesystem.hpp"
int main()
{
try
{
boost::filesystem::path source_path("/Codes/ThirdPartyWarning/source.txt");
boost::filesystem::path target_path("target.txt");
boost::filesystem::copy_file(source_path, target_path);
}
catch (const boost::filesystem::filesystem_error& e)
{
std::cerr << "Exception with Boost filesystem: " << boost::diagnostic_information(e) << std::endl;
return EXIT_FAILURE;
}
int a; // variable intentionally left to underline my point about warnings...
std::cout << "Value is " << a << std::endl;
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
Compiling this code with clang and `-Weverything` yields more than 1000 warnings at the time of this writing!
This example is tackled in Docker; see the [README](https://gitlab.inria.fr/formations/cpp/gettingstartedwithmoderncpp/blob/master/6-InRealEnvironment/Docker/ThirdPartyWarning/README.md) to see how to use this Docker image which illustrates directly the methods explained below.
%% Cell type:markdown id: tags:
## Indicating the header from those libraries as system ones
The first solution is to tell your build system that the directory in which the third party header files you need to include are system directories; for which compiler will not issue warnings.
This works obviously only if the headers from the third-party libraries are not located in same directories as those of your model, but this seems sensible enough not to be a hurdle.
The trick is to figure out how to give this instruction to your compiler:
%% Cell type:markdown id: tags:
### `clang`or `gcc` compiler
You need to replace the `-I` usually used to indicate the header directories by `-isystem`:
%% Cell type:markdown id: tags:
```shell
clang++ -Weverything -I /Codes/ThirdParty/opt/include simple_boost.cpp -L /Codes/ThirdParty/opt/lib -lboost_filesystem -lboost_system
```
becomes
```shell
clang++ -Weverything -isystem /Codes/ThirdParty/opt/include simple_boost.cpp -L /Codes/ThirdParty/opt/lib -lboost_filesystem -lboost_system
```
%% Cell type:markdown id: tags:
### CMake
You need to add the keyword `SYSTEM` in your target_include_directories. So
```
target_include_directories(simple_boost PUBLIC "/Codes/ThirdParty/opt/include")
```
becomes
```
target_include_directories(simple_boost SYSTEM PUBLIC "/Codes/ThirdParty/opt/include")
```
%% Cell type:markdown id: tags:
I must admit I don't usually go this way and uses up the second option, but I admit it seems much more straightforward than the second approach.
I must admit I don't usually go this way and instead use the second option, but I admit it seems quite straightforward.
The few drawbacks I can see are:
* You have to figure out how your build system takes this into account.
* You have to make sure it is portable: another user may not place these third-party headers in the same location as you, and you have to figure out a proper way to indicate where they are. This is by no mean overwhelming, but still requires a bit of work. This is especially so if the third party library is directly embedded in your code (might happen, especially if the library is header only).
* You have to make sure it is portable: another user may not place these third-party headers in the same location as you, and you have to figure out a proper way to indicate where they are. This is by no means overwhelming, but still requires a bit of work. This is especially so if the third party library is directly embedded in your code (might happen, especially if the library is header only).
%% Cell type:markdown id: tags:
## Deactivating locally warning with pragmas in your code
## Deactivating the warnings locally with pragmas in your code
%% Cell type:markdown id: tags:
Recently clang and gcc introduced **pragmas** to deactivate selectively warnings in part of your code.
The principle is that around the `#include` itself you provide preprocessor commands to deactivate temporarily the issue.
So for instance to solve the issue with clang, you may replace the include lines of the file by:
%% Cell type:code id: tags:
``` C++17
// Replace the two include lines in the program given previously
#pragma clang diagnostic push // starts the code block in which rules are modified
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wparentheses"
#pragma clang diagnostic ignored "-Wcast-qual"
#pragma clang diagnostic ignored "-Wdeprecated"
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#pragma clang diagnostic ignored "-Wweak-vtables"
#pragma clang diagnostic ignored "-Wundef"
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#pragma clang diagnostic ignored "-Wundefined-func-template"
#pragma clang diagnostic ignored "-Wc++98-compat"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wpadded"
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#include "boost/exception/diagnostic_information.hpp"
#include "boost/filesystem.hpp"
#pragma clang diagnostic pop // go back to normal rules
```
%% Cell type:markdown id: tags:
Yes, even in a top-notch library like Boost there are plenty of warnings! (but don't use that as an excuse not to clean-up yours...)
Unfortunately, you will surely have seen _clang_ is mentioned explicitly; gcc needs same kind of work... with the exact same syntax, compiler name aside. For instance if `-Wsign_conversion` is activated in your gcc build, you will need GCC pragma:
Unfortunately, you will surely have seen _clang_ is mentioned explicitly; gcc needs the same kind of work... with the exact same syntax, compiler name aside. For instance if `-Wsign_conversion` is activated in your gcc build, you will need GCC pragma:
%% Cell type:code id: tags:
``` C++17
#pragma GCC diagnostic ignored "-Wsign-conversion" // upper case GCC required to make it work!
```
%% Cell type:markdown id: tags:
To make things worse, unknown pragma triggers warning by itself, so either you need to deactivate _-Wunknown-pragmas_ or you need to use macros to separate compiler cases (see [this link](http://nadeausoftware.com/articles/2012/10/c_c_tip_how_detect_compiler_name_and_version_using_compiler_predefined_macros) to see how to indicate in a macro which compiler to use):
%% Cell type:code id: tags:
``` C++17
#ifdef __clang__
# pragma clang diagnostic push // starts the code block in which rules are modified
# pragma clang diagnostic ignored "-Wsign-conversion"
...
#pragma clang diagnostic pop // go back to normal rules
#elif !defined(__INTEL_COMPILER) and defined(__GNUG__)
# pragma GCC diagnostic push // starts the code block in which rules are modified
# pragma GCC diagnostic ignored "-Wsign-conversion"
...
#pragma GCC diagnostic pop // go back to normal rules
#endif
```
%% Cell type:markdown id: tags:
In my code I [fiddled a bit with macros](https://gitlab.inria.fr/MoReFEM/CoreLibrary/MoReFEM/raw/master/Sources/Utilities/Pragma/Pragma.hpp) to put in common macros with the same name and thus spare few lines.
%% Cell type:markdown id: tags:
### Extra level of indirection
There's common quote in C++ that almost every problem may be solved by adding an extra level of indirection. I recommend using it here if you go the pragma way: if several files in your project needs Boost filesystem to be included, it is better to define a header file of yours which will include the gory details with the pragmas. This way, you will repeat the Don't Repeat Yourself! principle and make your code more manageable (if one new warning appears after compiler update you need to change it at only one location) and also make your general code more readable, but substituting a big block by just one include line.
There's a common quote in the C++ community that almost every problem may be solved by adding an extra level of indirection. I recommend using it here if you go the pragma way: if several files in your project need the Boost filesystem to be included, it is better to define a header file of yours which will include the gory details with the pragmas. This way, you will repeat the Don't Repeat Yourself! principle and make your code more manageable (if one new warning appears after a compiler update you need to change it in only one location) and also make your code generally more readable, by substituting a big block by just one include line.
In my project, I include Boost filesystem with:
In my project, I include the Boost filesystem with:
```c++
#include "ThirdParty/IncludeWithoutWarning/Boost/Filesystem.hpp"
```
(yes, I'm not that good for naming stuff!)
%% Cell type:markdown id: tags:
[© Copyright](../COPYRIGHT.md)
......
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [C++ in a real environment](./0-main.ipynb) - [Namespaces](./5-Namespace.ipynb)
%% Cell type:markdown id: tags:
## Introduction
So far, we've dealt with snippets of code, and it was trivial not to make the mistake of naming two functions or classes with the same name. With a more sizable code, you might feel stuck at some point if you want to use the same name twice for different purposes... and with third party libraries it gets even worse: if this library already defines a function with the prototype you intended to define yourself, you're screwed.
So far, we've dealt with snippets of code, and it was trivial not to make the mistake of naming two functions or classes with the same name. With a more sizable codebase, you might feel stuck at some point if you want to use the same name twice for different purposes... and with third party libraries it gets even worse: if this library already defines a function with the prototype you intended to define yourself, you're screwed.
Even worse, if you use two libraries that both provide for instance their own spin over `Vector`, you are stuck as there will be ambiguity if these two classes are to be used in the same file.
Strangely enough, with C for which the issue may arise as well, no solution was provided to eschew this potential name clashing.
Fortunately, C++ introduced the **namespaces** for this: a **namespace** is an entity which groups together classes, functions and so on... So far, as we didn't define explicitly one we were actually in the **global namespace**.
## Defining a namespace and using its content
A namespace is just defined as a block prefixed by `namespace XXX` where XXX is the name of the namespace. Contrary to a class, a same namespace block may be used in as many file as you wish; declaration and definition may therefore be defined in separate files as usual.
A namespace is just defined as a block prefixed by `namespace XXX` where XXX is the name of the namespace. Contrary to a class, a namespace block with the same name can be used in as many files as you wish; declaration and definition may therefore be defined in separate files as usual.
%% Cell type:code id: tags:
``` C++17
// hpp file
#include <iostream>
namespace MyProject
{
void Hello();
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
// cpp file
#include <iostream>
namespace MyProject
{
void Hello()
{
std::cout << "Hello from MyProject namespace!" << std::endl;
}
} // namespace MyProject
```
%% Cell type:markdown id: tags:
To use the `Hello` function, you need to specify the namespace explicitly with the exact same syntax already used for classes:
%% Cell type:code id: tags:
``` C++17
Hello(); // COMPILATION ERROR
```
%% Cell type:code id: tags:
``` C++17
MyProject::Hello(); // OK
```
%% Cell type:markdown id: tags:
## Look-up rules
%% Cell type:markdown id: tags:
Now what's happen if two functions of the same name do exist? There are actually look-up rules: the compiler first look in the same namespace, and if it doesn't find any try one step toward the global namespace.
Now what happens if two functions of the same name do exist? There are actually look-up rules: the compiler first looks in the current namespace, and if it doesn't find any, it moves one step up toward the global namespace and retries.
This is the behaviour by default; you may choose explicitly to call another one with the `::` syntax (of the function begins by `::` this means you explicitly tell to go to the global namespace).
This is the behaviour by default; you may choose explicitly to call another one with the `::` syntax: `namespace::fun`. Using `::` with no prefix means you explicitly tell to go to the global namespace.
%% Cell type:code id: tags:
``` C++17
void Hello()
{
std::cout << "Hello from the global namespace!" << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
Hello();
```
%% Cell type:code id: tags:
``` C++17
MyProject::Hello();
```
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
void PrintHello()
{
Hello(); // Calls Hello() from MyProject namespace
::Hello(); // Calls Hello() from global namespace
}
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
MyProject::PrintHello();
```
%% Cell type:markdown id: tags:
## Layers of namespaces
You may in fact pile up several namespaces if you wish; the rules explained above do apply the same:
You may in fact pile up several namespaces if you wish; the rules explained above still apply the same:
%% Cell type:code id: tags:
``` C++17
// hpp file
namespace MyProject
{
namespace Internal
{
void DoInternalStuff();
} // namespace Internal
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
// cpp file
namespace MyProject
{
namespace Internal
{
void DoInternalStuff()
{
std::cout << "Do internal stuff!" << std::endl;
}
} // namespace Internal
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
void FunctionWithUsesUpInternalStuff();
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
void FunctionWithUsesUpInternalStuff()
{
Internal::DoInternalStuff();
}
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
MyProject::FunctionWithUsesUpInternalStuff();
```
%% Cell type:markdown id: tags:
Since C++ 17, you may in fact define several layers of namespasces in a same line:
%% Cell type:code id: tags:
``` C++17
namespace MyProject::Internal // should work only if notebook kernel set to C++ 17 and above!
{
void OtherInternalStuff();
} // namespace MyProject::Internal
```
%% Cell type:markdown id: tags:
## Good practice: use at least one namespace for your projects
To my mind namespaces are a big asset, and in my code I often use up several layers of namespaces. This enables proper ordering of the code, and also helps you in the naming of your functions and classes: sometimes it's handy to use the same name at different places in your code.
To my mind namespaces are a big asset, and in my code I often use several layers of namespaces. This enables proper ordering of the code, and also helps you in the naming of your functions and classes: sometimes it's handy to use the same name at different places in your code.
Your mileage may vary on this, but I really advise you to use at least one that would encompass all the content of your project. This way, you prevent the drawback I mentioned in the introduction about possible name conflict with a third-party library namesake function.
The `Internal` I used to illustrate layers is not innocent: it is also a common way to indicate end-users they shouldn't bother with some utility and/or internal functionalities. For instance in Boost library there is a namespace `detail`: any stuff defined in `boost::detail` namespace is clearly an indication you shouldn't bother except if you are implementing yourself a Boost library (I gather you would not be reading this tutorial if that is the case!)
%% Cell type:markdown id: tags:
## The `std` namespace
We've actually used one namespace extensively so far even if I didn't tell what it was: the `std::` prefix was just to indicate the namespace in which the STL constructs are defined.
We've actually used one namespace extensively so far even if I didn't say what it was: the `std::` prefix was just to indicate the namespace in which the STL constructs are defined.
There is a specific rule for that namespace: it is **forbidden** to try to add content in that namespace except for very specific case such as `std::hash` specialization (see [the cppreference page](https://en.cppreference.com/w/cpp/language/extending_std) for more details). Unfortunately, compilers will not tell you something is amiss; the standard says that doing so leads to undefined behaviour.
%% Cell type:markdown id: tags:
## Good practice: free functions closely associated to a class should be put in the same namespace
We saw [previously](../3-Operators/1-Intro.ipynb) that operators may be overloaded for your own types outside of the class (it is even mandatory to do so for some operators such [stream ones](../3-Operators/3-Stream.ipynb)).
The rule in this case is to define them in the **same namespace** as the one in which the class itself is defined. This way, you ensure the overload may be found even if the calling site is in an entirely different namespace.
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
class HoldAnInt
{
public:
HoldAnInt(int a);
void Print(std::ostream& out) const;
private:
int a_;
};
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
HoldAnInt::HoldAnInt(int a)
: a_{a}
{ }
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
void HoldAnInt::Print(std::ostream& out) const
{
out << a_ << std::endl;
}
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
std::ostream& operator<<(std::ostream& out, const HoldAnInt& obj)
{
obj.Print(out);
return out;
}
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
MyNamespace::HoldAnInt object(42);
std::cout << "The answer to life, the universe and everything is " << object << std::endl;
}
```
%% Cell type:markdown id: tags:
As we see, the overload is properly called even outside of `MyNamespace`.
As a rule, it is a good idea to put in the same namespace (and often even in the same file) the free functions that are closely related to a class.
%% Cell type:markdown id: tags:
## The `using` directive
Some people find it annoying to have to put explicitly the namespace scope in the call: they would prefer to use `cout` rather than `std::cout`.
There is a way to do so... with some caveats you absolutely need to know.
The way is to use the `using namespace` directive at the top of a file, at the same level as includes, which puts in the namespace from which it is called the content of the namespace specified in the command. For those of you familiar with Python, it is the same as `from MODULE import *`.
We'll illustrate this with `std`, which is a namespace like any other for stuff defined in the standard library.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
```
%% Cell type:code id: tags:
``` C++17
cout << "Without std:: prefix!"; // Won't work before following cell is activated
```
%% Cell type:code id: tags:
``` C++17
using namespace std;
```
%% Cell type:code id: tags:
``` C++17
cout << "Without std:: prefix!"; // Works! - or at least should work: Xeus Cling seems lost here...
```
%% Cell type:markdown id: tags:
The danger of doing so is that if all namespaces are used this way, we hinder completely the reason for which they were introduced in the first place!
The danger of doing so is that if all namespaces are used this way, we negate completely the reason for which they were introduced in the first place!
### Good practice: **never** use `using namespace` directive in header files
Naïvely, it might be tempting if you're allergic to typing `std::` everywhere to put in a header file once and for all `using namespace std`; this way, it is written only once and you only have to include this file to ensure it works. It respects the Don't Repeat Yourself mantra... Right?
Unfortunately, the big caveat in doing so is that you impose your choice on any end-user of the library that will include one of your header file in its project... rendering the namespace ineffective: its content is in this case everywhere in the global namespace.
So you should really use this only in source files.
%% Cell type:markdown id: tags:
### The softer `using` directive
The `using namespace` we saw imports all the symbols into the current namespace. It is possible however to just cherry-pick the command you wish with `using`:
%% Cell type:code id: tags:
``` C++17
namespace Example
{
void Foo() {}
void Bar() {}
} // namespace Example
```
%% Cell type:code id: tags:
``` C++17
using Example::Foo;
```
%% Cell type:code id: tags:
``` C++17
Foo(); // OK
```
%% Cell type:code id: tags:
``` C++17
Bar(); // COMPILATION ERROR
```
%% Cell type:markdown id: tags:
The caveats identified for `using namespace` remain true.
%% Cell type:markdown id: tags:
### Namespace shortcut
You may also in a file use a shortcut if you wish; you thus avoid the caveats we mentioned (as possible cost of readability if you abuse the feature). To do so, you use an assignment-like syntax with namespaces:
You may also in a file use a shortcut if you wish; you thus avoid the caveats we mentioned (at a possible cost in readability if you abuse the feature). To do so, you use an assignment-like syntax with namespaces:
%% Cell type:code id: tags:
``` C++17
{
namespace Ex = Example;
Ex::Bar(); // Ok
}
```
%% Cell type:markdown id: tags:
## Unnamed namespace
**Unnamed namespace** (the official name in the standard, but **anonymous namespace** is also commonly used) is just a namespace... without name (quite underwhelming a reveal I surmise!)
They may be used in **source** files to define helper functions or classes that are used only in the scope of this source file.
%% Cell type:code id: tags:
``` C++17
// AnySourceFile.cpp
namespace // anonymous
{
void InternalStuff(); // This function exists in the cpp file but its symbol is not
// exported - exactly as C static function we saw earlier.
} // namespace anonymous
```
%% Cell type:markdown id: tags:
Doing so prevent issues if in several source files you define helper functions with the exact same name, which may cause linking error if not in the unnamed namespace (see the first two replies of [this StackOverflow thread](https://stackoverflow.com/questions/357404/why-are-unnamed-namespaces-used-and-what-are-their-benefits) if you want to learn more about them).
Doing so prevents issues if you define helper functions with the exact same name in different source files, which may cause linking error if not in the unnamed namespace (see the first two replies of [this StackOverflow thread](https://stackoverflow.com/questions/357404/why-are-unnamed-namespaces-used-and-what-are-their-benefits) if you want to learn more about them).
If you have a C background, you may have used `static functions` in this case; in C++ the unnamed namespace is recommended instead.
Unnamed namespace should be used in compiled files; in header files their purpose would be defeated...
%% Cell type:markdown id: tags:
## Side note: how to include C header
Namespaces are in fact the reason the C header are included with a name like `#include <cmath>` rather than `#include <math.h>`:
* With `#include <cmath>`, symbols are deemed to be in `std` namespace and possibly in the global namespace as well.
* With `#include <math.h>`, symbols are in global namespace and possibly in `std` namespace as well.
More on this in this [StackOverflow thread](https://stackoverflow.com/questions/10460250/cstdio-stdio-h-namespace); the bottom line is that you should really use the first form intended for C++.
%% Cell type:markdown id: tags:
[© Copyright](../COPYRIGHT.md)
......
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [C++ in a real environment](./0-main.ipynb) - [External tools](./6-Tools.ipynb)
%% Cell type:markdown id: tags:
## Introduction
The purpose of this notebook is just to namedrop briefly some facilities related to C++ that might be useful.
It's far from exhaustive - I won't present debuggers as usually they are provided directly with your IDE (and if you're a Vim or Emacs user you probably already familiar with [gdb](https://www.gnu.org/software/gdb/)).
It's far from exhaustive - I won't present debuggers as usually they are provided directly with your IDE (and if you're a Vim or Emacs user you're probably already familiar with [gdb](https://www.gnu.org/software/gdb/)).
Some tools are redundant, but they are complementary: the more you may be able to use the better. The constraint is time: it is not straightforward to use some of those, and even for the user-friendly ones there is a non neglictible set-up time. But for projects that are gaining traction it becomes a no-brainer at some point to set-up those properly once and for all - usually part of [continuous integration](https://gitlab.inria.fr/FormationIntegrationContinue/gitlabciintroduction) process.
Some tools are redundant, but they are complementary: the more you use the better. The constraint is time: it is not straightforward to use some of those, and even for the user-friendly ones there is a non negligible set-up time. But for projects that have gained enough traction, at some point it becomes a no-brainer to set them up properly once and for all - usually part of the [continuous integration](https://gitlab.inria.fr/FormationIntegrationContinue/gitlabciintroduction) process.
## Online compilers
This [GitHub page](https://arnemertz.github.io/online-compilers/) gives a list of online compilers we mentioned several times in this tutorial.
I have used only [Coliru](http://coliru.stacked-crooked.com/) which provides an API to incorporate directly some interactive code in a Web page (disclaimer - I have not used that feature yet) and [Wandbox](http://melpon.org/wandbox) which may be useful in day-to-day work as you may try a snippet of code with many configurations. It's useful for instance when you intend to use a bleeding-edge feature to see which version of compilers support it or not. There are more advanced options as [Compiler Explorer](https://godbolt.org/) which allows to explore the intermediate Assembly code generated, this is a very advanced utility that may be useful to optimize your code and to understand how a compiler internally works.
## Static analyzer tools
[cppcheck](http://cppcheck.sourceforge.net/) is a static analyser program; it might really help you to find some questionable constructs in your code. As many tools, it's not 100 % accurate and sometimes it may raise false positives, but I nonetheless recommend it warmly.
[cpplint](https://github.com/cpplint/cpplint) is also worth a look, we mentioned it for instance in [this chapter](./2-FileStructure.ipynb) to track down missing includes in files.
[clang-tidy](https://clang.llvm.org/extra/clang-tidy/) is also used by some of us. [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) also includes advanced utilities to automatically refactor/correct/improve code. See the following [link](https://gitlab.inria.fr/formations/cpp/gettingstartedwithmoderncpp/-/wikis/How-to-use-Clang-Tidy-to-automatically-correct-code).
## Valgrind
[Valgrind](valgrind.org/) is mostly known as a leak checker, but is really a jack-of-all-trade tool; callgrind for instance may be used to profile your code and see where most of the computation time is spent. Some lesser-known tools are also incredibly useful: [Verrou](https://github.com/edf-hpc/verrou) for instance, developed at EDF, is a floating-point checker which helps you figure out if at some point some of your computations might be skewed by the way the computer approximates the real numbers.
Unfortunately, macOS support is scarse: sometimes they plainly say it is not up-to-date, but even when they say it works the outputs were never satisfactory for me (always check `valgrind ls` first as they recommend on their website...). So even if I develop mostly in macOS I always fire up valgrind in a Linux environment.
## Address sanitizer
A [recent competitor](https://github.com/google/sanitizers/wiki/AddressSanitizer) (once again use both if possible!) to Valgrind, which has the advantage of running with a much better runtime (Valgrind slows down your program tremendously). Might be integrated in some IDEs (it is in XCode on macOS).
## Sonarqube
[Sonarqube](https://www.sonarqube.org/) is a development platform which inspects your code and helps to figure out where there are issues. It enables integration of multiple tools such as cppcheck mentioned earlier. If you're Inria staff, an instance was set by our [Bordeaux colleagues](http://sed.bordeaux.inria.fr/) and is [available](https://sonarqube.inria.fr/) for you.
For open-source projects, the company behind Sonarqube provides a [freely accessible platform]( https://sonarcloud.io/about).
## Tests
These are frameworks to write your tests; to actually run them you should see what your build system provides (for instance CMake comes with CTest which takes gracefully the management of tests in your project).
### Google test
[GoogleTest](https://google.github.io/googletest/) includes everything that is needed for testing: [xUnit](https://en.wikipedia.org/wiki/XUnit), test discovery, rich test assertions, death tests, XML test report generation, ...
It is also the only one we know that provide mocks. It is worth saying that mocking in C++ requires your application to be specially designed for it, more information are available on the [gMock cookbook](https://google.github.io/googletest/gmock_cook_book.html).
### Doctest
[doctest](https://github.com/onqtam/doctest) is another C++ testing framework that was very appreciated by one of the teacher (Vicente) in the 2021 session of this tutorial.
### BoostTest
[Boost test](https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/index.html) is a widely used facility to write unit and integration tests with your code.
It might be used as a header-only or as a compiled library; for big projects they recommend using the latter.
### Catch2
[Catch2](https://github.com/catchorg/Catch2) is a more recent test facility which is aimed at being user-friendly.
## Build system
Already mentioned [here](./1-SetUpEnvironment.ipynb#Build-system).
## Code formatters
It is useful to delegate the check on your code format to an external tool, which may take a bit of time to configurate to suit your needs.
I can mention [Uncrustify](http://uncrustify.sourceforge.net/): plenty of options to configure, even if they're not easy to figure out.
[clang-format](https://clang.llvm.org/docs/ClangFormat.html) probably provides a better trade-off power/complexity but requires LLVM clang to be installed on your system (AppleClang won't do, but you can easily install it on macOS with Homebrew).
## Doxygen
[Doxygen](http://www.doxygen.nl/) is a software to write an automatic documentation for your functions / classes / types / you name it.
It is rather easy to set up to easy task but may become a tad more difficult once you want to tackle more advanced features. Nonetheless it is the de-facto standard for C++ documentation and it's really something you should set up quite early if you're working on a project meant to stay for a while: it's really a hassle to spend countless hours providing after the fact such guidance in the code. As for compilers, you should strive to provide a documentation without any warning.
## Codespell
[codespell](https://github.com/codespell-project/codespell) is a very neat project to check english errors in your code. It may be used upon code directly or upon the associated README and is rather helpful to notice typos or some english errors (for instance it found out we used _informations_ a lot in these notebooks whereas it is incorrect and should be _information_).
A strong point for this tool is that it is very easy to use; no steep learning curve for it.
We wouldn't however advice to use the options to automatically correct your documents; there might be some false positive.
We wouldn't however advice to use the options to automatically correct your documents; there might be some false positives.
## Ccache
[Ccache](https://ccache.dev/) is a great tool that caches compilation results and does it very well even when you work with different git branches, for instance. If your compilation time, even if you compile in parallel mode, is getting annoyingly too long, this tool is recommended. Ccache is also quite well integrated with CMake.
[Ccache](https://ccache.dev/) is a great tool that caches compilation results and does it very well even when you work with different git branches, for instance. If your compilation time, even if you compile in parallel mode, is getting too annoyingly long, this tool is recommended. Ccache is also quite well integrated with CMake.
## More...
Our colleagues at Inria Bordeaux also recommend some additional tools in the Inria SonarQube
[documentation](https://sonarqube.inria.fr/pages/documentation.html#orgd4ab5b1).
We also mentioned in a notebook [include-what-you-use](https://include-what-you-use.org/) to help figure out which include declarations are truly required and when forward declaration may be used.
%% Cell type:markdown id: tags:
[© Copyright](../COPYRIGHT.md)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment