Commit d9bbf1f5 authored by CHAMONT David's avatar CHAMONT David
Browse files

Début de préparation des sessions Cpp On Demand

parent 5ab0803b
Also install `git-lfs`.
#include <iostream>
template < typename T >
class Ref {
public :
Ref( T & data ) { m_pdata = &data ; }
void operator=( T data ) { *m_pdata = data ; }
private :
T * m_pdata ;
} ;
template < typename T >
Ref<T> make_ref( T & data )
{ return Ref<T>(data) ; }
template < typename T >
void f( T data )
{ data = 42 ; }
int main()
{
int i = 0, j = 0 ;
f(i) ;
f(make_ref(j)) ;
std::cout<<i<<" "<<j<<std::endl ;
return 0 ;
}
#include <iostream>
template < typename T >
class Ref
{
public :
Ref( T & data ) : m_data(data) {}
void operator=( T data ) { m_data = data ; }
private :
T & m_data ;
} ;
template < typename T >
Ref<T> make_ref( T & data )
{ return Ref<T>(data) ; }
template < typename T >
void f( T data )
{ data = 42 ; }
int main()
{
int i = 0, j = 0 ;
f(i) ;
f(make_ref(j)) ;
std::cout<<i<<" "<<j<<std::endl ;
return 0 ;
}
//
// Modify the class Ref, and only this class, so that
// the call to f(make_ref(j)) modify the value of j,
// although f(i) does not modify i.
// The final display should be : 0 42
//
#include <iostream>
template < typename T >
class Ref
{
public :
Ref( T data ) { m_data = data ; }
void operator=( T data ) { m_data = data ; }
private :
T m_data ;
} ;
template < typename T >
Ref<T> make_ref( T & data )
{ return Ref<T>(data) ; }
template < typename T >
void f( T data )
{ data = 42 ; }
int main()
{
int i = 0, j = 0 ;
f(i) ;
f(make_ref(j)) ;
std::cout<<i<<" "<<j<<std::endl ;
return 0 ;
}
# 1. Basic challenge
## What is displayed ?
```cpp
#include <iostream>
struct MyInteger {
MyInteger( int i = 0 ) : i_(i) {}
int i_ ;
}
int main() {
MyInteger mi() ;
std::cout<<mi.i_<<std::endl ;
}
```
1. nothing, it will fails to compile MyInteger
2. there will be an error at runtime
3. random anything
4. 0
5. nothing, it will fails to compile main()
## **Good** and *bad* answers
* **1**: the final `;` is lacking
* *3* : the only constructor of the class always set `i_` to a value ; there is no reason it may be random.
* **5**: the famous "most vexing parse" ! `MyInteger mi() ;` is interpreted as a function declaration, not an object construction.
# 2. A dynamic memory challenge
## May I copy such an array ?
```cpp
class Array {
public :
explicit Array( unsigned s ) { data_ = new double [s]; }
double & operator[]( unsigned i ) { return data_[i]; }
~Array() { delete data_; }
private :
double * data_;
} ;
```
1. yes, the copy constructor is added by the compiler
2. no, the operator=() is lacking
3. no, the copy constructor is lacking
4. core dumped or segmentation fault ensured
5. yes, but there is a memory leak
6. yes, thanks to explicit
## **Good** and *bad* answers
* *1* : the implicit copy constructor is perforing a shallow copy, not relevant here.
* **2** and **3** : when dynamic rsources are involved (here heap memory), one must redefine the copy constructor, the copy assignment operator... and also now the move constructor and the move assignment operator.
* **4** : also true !
* *5* : the problem here is not memory leak, but shared memory which will be deleted several times.
* *6* : off-topic ; `explicit` is there in order to forbid an implicit compiler conversion between an `unsigned` and an `Array`.
# 3. About implicit conversions with predefined numerical types
## What is displayed ?
```cpp
#include <iostream>
int main()
{
int i = -1 ;
unsigned int j = 1 ;
if ( i < j ) std::cout << "i is less than j" ;
else std::cout << "i is greater than j" ;
return 0 ;
}
```
1. there will be a compilation error
2. there will be an error at runtime
3. i is less than j
4. i is greater than j
## **Good** and *bad* answers
* **4** : when interpreting `i < j`, the compiler convert all the numbers `unsigned int`, which it considers more accurate than `int`, then the value `-1` becomes the highest possibel value than an `unsigned int` can have, therefore greater than `1`. Damned !
# 4. An object-oriented challenge
## What is displayed ?
```cpp
#include <iostream>
struct A {
void m( double d )
{ std::cout<<d<<std::endl ; }
} ;
struct B : public A {
void m( int i )
{ std::cout<<i<<std::endl ; }
} ;
int main()
{
B b;
b.m(3.14);
return 0;
}
```
1. there will be a compilation error
2. there will be an error at runtime
3. 3
4. 3.14
## **Good** and *bad* answers
* **3** : when interpreting `b.m(3.14)`, the compiler find some `m` function within class `B`, and then does not check if the base class has some better matching function.
# 5. A floating point challenge
## Why is it returning false ?
```cpp
bool check()
{
float ref, index ;
ref = 169.0 / 170.0 ;
for ( index = 160.0 ; index < 170.0 ; index += 1.0 )
if ( ( index / ( index + 1.0 ) ) == ref )
return true ;
return false ;
}
```
1. never iterate with a real variable (index)
2. never compare floats with ==
3. some curly braces are lacking
4. 1.0 is a double
5. index value is never 169.0
## **Good** and *bad* answers
* *1* : "never" is a little string, one can iterate with anything iterable, and sometimes need to.
* **2** : as a general rule, floating operation are never perfectly exact ; any floating comparison should allow an epsilon error.
* *3* : some curly braces would make the code more readable, but that is not the issue here.
* **4** : best answer here, because `1.0` is a double, the result of `index / ( index + 1.0 )` is a double, and will not match the value stored in `ref`, which is a float ; try with `1.0f`, or `static_cast<float>( index / ( index + 1.0 ) )`.
* *4* : nice try ;) actually no, because the step of `1.0` is 2 power 0, it can be exactly encoded in binary basis, and the last value of `index` will exactly be `169.0` ; if the step was `0.1`, for example, the value of ìndex` would never be exactly `169.0`.
# 6. A polymorphisme challenge
## What is lacking below ?
class A {\
public :
A() {}
~A() {}
virtual void display() = 0 ;
} ;
1. one "virtual"
2. several "virtual"
3. a "private" zone
4. the body of display()
## **Good** and *bad* answers
* **1**: the destructor should be virtual, because `display()` is.
* *3* : a private zone is not mandatory.
* *4* : off-topic ; that is an abstract class, not all the functions needs to be implemented.
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Demonstration notebook\n",
"\n",
"A jupyter notebook is a sequence of cells. Some of them are for textual explanations, and written in markdown. Other cells are extracts code, and can be executed on the fly, thanks to the associated **kernel** attached to the notebook.\n",
"\n",
"The jupyter notebooks were initially designed for interpreted language, such as python, but the QuantStack company provides the [xeus-cling](https://xeus-cling.readthedocs.io/en/latest/) kernels, which extend those notebooks to C++. This notebook is associated to a C++17 kernel.\n",
"\n",
"When you use a notebook, you can interactively select any cell, *double-click* on it, and edit its content. Then, when you *shift-return*, a markdown cell will be rendered, and a code cell will be executed on the associated `kernel`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code executed with the xeus-cling kernel\n",
"\n",
"The recent releases of xeus-cling have made huge progress, so to marry the \"compiled\" nature of C++ with the \"interpreted\" nature of notebook cells. Yet, you will encounter during this lesson some old notebooks, with tricks which were necessary with the old kernels."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You should consider all the code cells of a given notebook are subsets of a single big C++ program. In C++, one cannot declare several time the same variable. As a consequence, it was initially impossible to reuse the same variable name in several cells. It was even impossible to replay the same cell several times ! In order to bypass this limit, we often enclose each cell in a `{}` block, creating a local scope:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"#include <iostream>"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"i is greater than j"
]
}
],
"source": [
"{\n",
" int i = -1 ;\n",
" unsigned int j = 1 ;\n",
" if ( i < j ) std::cout << \"i is less than j\" ;\n",
" else std::cout << \"i is greater than j\" ;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Also, until recently, one had to make an independant cell for each independent function, and you may encounter such notebooks where we have split the code in many many cells."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Restarting the kernel\n",
"\n",
"When it fails to compile some wrong code, the kernel may switch in a *corrupted* state, being unable to compile anything more. When you suspect such a situation, **restart the kernel** with the relevant button above. This can be done any time, on purpose, when you want to restart your execution from a blank page."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code executed in the underlying machine"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sometimes, we want to demonstrate some modern C++ features which are not yet supported by the notebook kernel, but may be compiled with the underlying computer where your jupyter server is running. Also, sometimes, we need some additional libraries which are not accessible to the notebook kernel, or we want to split our code into several files and demonstrate how those files interact."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The cell below, with its magic first line `%%file`, will generate a file in the underlying file system, which you can see if you look at the file browser on the left of this window, or if you start a terminal."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Writing tmp.ref.cpp\n"
]
}
],
"source": [
"%%file tmp.ref.cpp\n",
"\n",
"#include <iostream>\n",
"\n",
"template < typename T >\n",
"class Ref\n",
"{\n",
"public :\n",
" Ref( T data ) { data_ = data ; }\n",
" void operator=( T data ) { data_ = data ; }\n",
"private :\n",
" T data_ ;\n",
"} ;\n",
"\n",
"template < typename T >\n",
"Ref<T> make_ref( T & data )\n",
"{ return Ref<T>(data) ; }\n",
"\n",
"template < typename T >\n",
"void f( T data )\n",
"{ data = 42 ; }\n",
"\n",
"int main()\n",
"{\n",
" int i = 0, j = 0 ;\n",
" f(i) ;\n",
" f(make_ref(j)) ;\n",
" std::cout<<i<<\" \"<<j<<std::endl ;\n",
" return 0 ;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The cells below, with their magic prompt `!`, are executing bash commands in the underlying folder, and display their outputs."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"!rm -f tmp.ref.exe && g++ -std=c++03 tmp.ref.cpp -o tmp.ref.exe"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 0\n"
]
}
],
"source": [
"!./tmp.ref.exe"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "C++17",
"language": "C++17",
"name": "xcpp17"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".cpp",
"mimetype": "text/x-c++src",
"name": "c++",
"version": "17"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
# Get in touch
## Breaking the ice
* Who are the trainers ?
* Quizz
* Who are the trainees ?
* Forum/chat tool
* Git repository
## Exercise
As a warm-up, we suggest you try an exercise using the web compiler [CoLiRu](http://coliru.stacked-crooked.com/) (use the `-std=c++03` option).
Copy the code below into the [CoLiRu web page](http://coliru.stacked-crooked.com/). Make sure the compiler command, at the bottom of the age, has the option `-std=c++03`. Then push the button "Compile, link and run...". The output should be `0 0`: the `f()` function takes its input argument by value, and modify the value, ont the original variable.
We would like the `make_ref()` function wrap its argument in an instance of `Ref`, in such a way that the call to `f(make_ref(j))` will really `j`. Modify the class `Ref`, and only this class, so to make that happens. The output should be `0 42`.
Play !
```cpp
#include <iostream>
template < typename T >
class Ref
{
public :
Ref( T data ) { data_ = data ; }
void operator=( T data ) { data_ = data ; }
private :
T data_ ;
} ;
template < typename T >
Ref<T> make_ref( T & data )
{ return Ref<T>(data) ; }
template < typename T >
void f( T data )
{ data = 42 ; }
int main()
{
int i = 0, j = 0 ;
f(i) ;
f(make_ref(j)) ;
std::cout<<i<<" "<<j<<std::endl ;
return 0 ;
}
```
## How to use C++ notebooks ?
Once Jupyter is installed on your machine, do not forget to
add the xeus-cling kernels for C++:
* `conda install -c conda-forge xeus-cling`
Then, you can start either the simple old-fashioned server,
or the more complete jupyter laboratory :
* `jupyter notebook`
* `jupyter-lab`
The notebooks are files whose name ends with `.ipynb`. Try to read/execute `en.demo.ipynb`.
## If you use Docker
In case you prefer to use Docker, there is a script provided with
the course material, which helps you start a container. Within a
`bash` shell, move to the top folder of *ModernScientificCpp*,
and type :
* `./Etc/docker/run-8888.sh 4nages default`
Then you can start the old-fashioned server or the jupyter laboratory,
with some few additional options, required for Docker:
* `jupyter notebook --no-browser --allow-root --ip=0.0.0.0`
* `jupyter-lab --no-browser --allow-root --ip=0.0.0.0`
\ No newline at end of file
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Cahier computationnel de démonstration\n",
"\n",
"Un cahier computationnel est une séquence de cellules. Certaines d'entre elles sont destinées à fournir des explications textuelles, et sont écrites en markdown. D'autres sont des extraits de code, et peuvent être exécutées à la volée, grâce au **noyau** associé au cahier.\n",
"\n",
"Les cahiers Jupyter ont été initialement conçus pour des langages interprétés, tels que python, mais la société QuantStack fournit les noyaux [xeus-cling](https://xeus-cling.readthedocs.io/en/latest/), qui étendent ces cahiers au C++. Le cahier présent est associé à un noyau C++17.\n",
"\n",
"Lorsque vous utilisez un cahier, vous pouvez sélectionner interactivement n'importe quelle cellule, *double-cliquer* dessus et modifier son contenu. Ensuite, lorsque vous faites *shift-return*, une cellule markdown sera affichée avec sa mise en page, alors qu'une cellule de code sera exécutée à l'aide du noyau associé."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code exécuté avec le noyau xeus-cling\n",
"\n",
"Les versions récentes de xeus-cling ont fait d'énormes progrès, afin de marier la nature \"compilée\" du C++ avec la nature \"interprétée\" des cellules des cahiers. Cependant, vous rencontrerez au cours de cette leçon quelques vieux cahiers, avec des astuces qui étaient nécessaires avec les anciens noyaux."
]
},
{
"cell_type": "markdown",
"metadata": {},