Mentions légales du service

Skip to content
Snippets Groups Projects
Commit d999971c authored by DIAZ Jerome's avatar DIAZ Jerome
Browse files

Very minor corrections in part 6.

parent a06cec63
No related branches found
No related tags found
1 merge request!1092024 Jérôme reread
%% 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.
- `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 `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 now PyCharm by the same company)
* [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).
- 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!
%% 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.
* [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 your t
%% 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 compiler with which you may test your code, the merrier!
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.
### 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:
* 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
```
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.
## 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`
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....
Luckily, both clang and gcc are shipped 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).
%% 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.
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).
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:
* 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.
## 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.
The few drawbacks I can see are:
* You have to figure out how your build system take this into account.
* 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).
%% Cell type:markdown id: tags:
## Deactivating locally warning 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:
%% 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.
In my project, I include 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.
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.
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.
%% 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.
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).
%% 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:
%% 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.
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.
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!
### 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:
%% 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).
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)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment