Commit 8b83636e authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Remove error in exercice 24 text.

parent 9a15e45f
......@@ -121,7 +121,6 @@
"* Minimum maximum number of bits to consider.\n",
"* Increment over the number of bits\n",
"* Maximum maximum number of bits to consider.\n",
"* The resolution.\n",
"* A `TestDisplay` object (per reference).\n",
"\n",
"The `main()` should now be:"
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Object programming](/notebooks/2-ObjectProgramming/0-main.ipynb) - [TP 7](/notebooks/2-ObjectProgramming/6b-TP.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-22:-base-class-TestDisplay" data-toc-modified-id="EXERCICE-22:-base-class-TestDisplay-1">EXERCICE 22: base class <code>TestDisplay</code></a></span></li><li><span><a href="#EXERCICE-23:-inherit-from-TestDisplayPowerOfTwoApprox" data-toc-modified-id="EXERCICE-23:-inherit-from-TestDisplayPowerOfTwoApprox-2">EXERCICE 23: inherit from <code>TestDisplayPowerOfTwoApprox</code></a></span></li><li><span><a href="#EXERCICE-24:-loop()-function" data-toc-modified-id="EXERCICE-24:-loop()-function-3">EXERCICE 24: loop() function</a></span></li><li><span><a href="#EXERCICE-25:-make-loop()-a-class" data-toc-modified-id="EXERCICE-25:-make-loop()-a-class-4">EXERCICE 25: make <code>loop()</code> a class</a></span></li><li><span><a href="#EXERCICE-26:-store-TestDisplay-objects-in-Loop" data-toc-modified-id="EXERCICE-26:-store-TestDisplay-objects-in-Loop-5">EXERCICE 26: store <code>TestDisplay</code> objects in <code>Loop</code></a></span></li><li><span><a href="#EXERCICE-27:-simplification" data-toc-modified-id="EXERCICE-27:-simplification-6">EXERCICE 27: simplification</a></span></li><li><span><a href="#EXERCICE-28:-dynamic-allocation-of-array" data-toc-modified-id="EXERCICE-28:-dynamic-allocation-of-array-7">EXERCICE 28: dynamic allocation of array</a></span></li><li><span><a href="#EXERCICE-29:-move-ownership-into-Loop" data-toc-modified-id="EXERCICE-29:-move-ownership-into-Loop-8">EXERCICE 29: move ownership into <code>Loop</code></a></span></li><li><span><a href="#EXERCICE-30:-transform-Loop-into-TestDisplayContainer" data-toc-modified-id="EXERCICE-30:-transform-Loop-into-TestDisplayContainer-9">EXERCICE 30: transform <code>Loop</code> into <code>TestDisplayContainer</code></a></span></li></ul></div>
%% Cell type:markdown id: tags:
### EXERCICE 22: base class `TestDisplay`
Create a base class `TestDisplay` from which both `TestDisplayPowerOfTwoApprox` and `TestDisplaySum` will inherit publicly.
This class:
* Should get a constructor which sets the resolution.
* Includes a protected method named `PrintNumericalError()` that will replace the `print_numerical_error()` we introduced in previous exercice.
The constructors of derived classes will of course have to be modified accordingly: so far we relied on default ones.
%% Cell type:markdown id: tags:
### EXERCICE 23: inherit from `TestDisplayPowerOfTwoApprox`
We would like to get back former output in which we got first all outputs for 0.65, then all the ones for 0.35.
To do so, we will create two classes `TestDisplayPowerOfTwoApprox065` and `TestDisplayPowerOfTwoApprox035` that inherits from `TestDisplayPowerOfTwoApprox`.
Of course, we still abide by the DRY principle and we want to specialize only the code related to `Do()` method.
The `main()` to use:
%% Cell type:code id: tags:
``` C++17
int main(int argc, char** argv)
{
static_cast<void>(argc); // to silence warning about unused argc - don't bother
static_cast<void>(argv); // to silence warning about unused argv - don't bother
TestDisplayPowerOfTwoApprox065 test_display_approx065(100);
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
test_display_approx065.Do(Nbits);
std::cout << std::endl;
TestDisplayPowerOfTwoApprox035 test_display_approx035(100);
for (int Nbits = 2; Nbits <= 8; Nbits += 2)
test_display_approx035.Do(Nbits);
std::cout << std::endl;
TestDisplaySum test_display_sum(1000);
for (int Nbits = 1; Nbits <= 8; ++Nbits)
test_display_sum.Do(Nbits);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
_Expected output:_
````
[With 2 bits]: 0.65 ~ 0.75 (3 / 2^2) [error = 15/100]
[With 4 bits]: 0.65 ~ 0.625 (10 / 2^4) [error = 4/100]
[With 6 bits]: 0.65 ~ 0.65625 (42 / 2^6) [error = 1/100]
[With 8 bits]: 0.65 ~ 0.648438 (166 / 2^8) [error = 0/100]
[With 2 bits]: 0.35 ~ 0.375 (3 / 2^3) [error = 7/100]
[With 4 bits]: 0.35 ~ 0.34375 (11 / 2^5) [error = 2/100]
[With 6 bits]: 0.35 ~ 0.351562 (45 / 2^7) [error = 0/100]
[With 8 bits]: 0.35 ~ 0.349609 (179 / 2^9) [error = 0/100]
[With 1 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 2965 [error = 254/1000]
[With 2 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4448 [error = 119/1000]
[With 3 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4008 [error = 8/1000]
[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 30/1000]
[With 5 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3967 [error = 2/1000]
[With 6 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 4004 [error = 7/1000]
[With 7 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3977 [error = 0/1000]
[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2/1000]
````
%% Cell type:markdown id: tags:
### EXERCICE 24: loop() function
Create a `loop()` function which takes as arguments:
* Minimum maximum number of bits to consider.
* Increment over the number of bits
* Maximum maximum number of bits to consider.
* The resolution.
* A `TestDisplay` object (per reference).
The `main()` should now be:
%% Cell type:code id: tags:
``` C++17
int main(int argc, char** argv)
{
static_cast<void>(argc); // to silence warning about unused argc - don't bother
static_cast<void>(argv); // to silence warning about unused argv - don't bother
TestDisplayPowerOfTwoApprox065 test_display_approx065(100);
TestDisplayPowerOfTwoApprox035 test_display_approx035(100);
TestDisplaySum test_display_sum(1000);
loop(2, 8, 2, test_display_approx065);
loop(2, 8, 2, test_display_approx035);
loop(1, 8, 1, test_display_sum);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
To do so, you will need to define `Do()` as a virtual method in `TestDisplay()`. As `TestDisplay` is not meant to be instantiated directly, make sure to make this class pure virtual.
%% Cell type:markdown id: tags:
### EXERCICE 25: make `loop()` a class
Replace `loop()` by a class `Loop` with a method `Do()` which is called.
%% Cell type:markdown id: tags:
### EXERCICE 26: store `TestDisplay` objects in `Loop`
Add to the `Loop` class:
* A data attribute to store 5 `TestDisplay` objects: `TestDisplay* test_display_list_[5]`. We will store them as pointers for two reasons:
* There are no default constructor for `TestDisplay`, so the compiler would have yelled.
* We want polymorphism to be put in motion, and it requires either pointers or references.
* A method to register an array in this internal array, with prototype:
````
void Register(int position, TestDisplay test_display);
````
where `position` is the index at which `test_display` will be stored. This function should of course check `position` is valid; if not use the following function:
%% Cell type:code id: tags:
``` C++17
[[noreturn]] void error(std::string explanation)
{
std::cout << "ERROR: " << explanation << std::endl;
exit(EXIT_FAILURE);
}
```
%% Cell type:markdown id: tags:
`Loop::Do()` won't take any longer a `TestDisplay` argument but instead act upon all the ones stored in the data attribute; if nullptr is found for an object it will be skipped (so don't forget to correctly set them initially to this value!)
`main()` to use is:
%% Cell type:code id: tags:
``` C++17
int main(int argc, char** argv)
{
static_cast<void>(argc); // to silence warning about unused argc - don't bother
static_cast<void>(argv); // to silence warning about unused argv - don't bother
TestDisplayPowerOfTwoApprox065 test_display_approx065(100);
TestDisplayPowerOfTwoApprox035 test_display_approx035(100);
TestDisplaySum test_display_sum(1000);
Loop loop;
loop.Register(0, &test_display_approx065);
loop.Register(1, &test_display_approx035);
loop.Register(3, &test_display_sum);
loop.Do(4, 16, 4);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
Output is modified: with current code we impose same bit range for all `TestDisplay`:
%% Cell type:markdown id: tags:
````
[With 4 bits]: 0.65 ~ 0.625 (10/2^4) [error = 38462/1000000]
[With 8 bits]: 0.65 ~ 0.648438 (166/2^8) [error = 2404/1000000]
[With 12 bits]: 0.65 ~ 0.649902 (2662/2^12) [error = 150/1000000]
[With 16 bits]: 0.65 ~ 0.649994 (42598/2^16) [error = 9/1000000]
[With 4 bits]: 0.35 ~ 0.34375 (11/2^5) [error = 17857/1000000]
[With 8 bits]: 0.35 ~ 0.349609 (179/2^9) [error = 1116/1000000]
[With 12 bits]: 0.35 ~ 0.349976 (2867/2^13) [error = 70/1000000]
[With 16 bits]: 0.35 ~ 0.349998 (45875/2^17) [error = 4/1000000]
[With 4 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3857 [error = 29930/1000000]
[With 8 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3968 [error = 2012/1000000]
[With 12 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]
[With 16 bits]: 0.65 * 3515 + 0.35 * 4832 = 3976 ~ 3975 [error = 252/1000000]
````
%% Cell type:markdown id: tags:
### EXERCICE 27: simplification
Let's face it: the possibility to place the `TestDisplay` where we want is a bit overkill: we just need to iterate through all objects and don't really need to know the exact position of each of them.
So we will:
* Remove the position argument; each new `TestDisplay` object registered is added to the first position available.
* A new data attribute `next_position_available_` will tell which is not yet used.
* The error handling will now check `next_position_available_` has not reached 5 when a new `Register()` call is done.
%% Cell type:markdown id: tags:
### EXERCICE 28: dynamic allocation of array
Instead of setting an arbitrary size of 5, we will now add a size dynamically in `Loop` constructor; the internal storage will now be:
````
TestDisplay** test_display_list_;
````
meaning we will store an array of pointers (don't worry, we will see later how to avoid such monstruosities... but it is useful nonetheless to try them a bit).
Constructor must now:
* Allocate the array of `TestDisplay*` with a size given as its argument.
* Keep track of the size (the related data attribute should be constant: we don't intend to modify the size of the array after construction).
* Set each element to `nullptr`.
Destructor must of course take care of deallocating properly the memory.
**NOTE:** To avoid a warning you should use `std::size_t` when allocating the array: this is the type used for array (which is in all compilers I've used an alias to `unsigned long`).
%% Cell type:markdown id: tags:
### EXERCICE 29: move ownership into `Loop`
Currently `Loop` use pointers to underlying `TestDisplay` objects but do not own them: it is not in charge of deallocating them. This may prove dangerous in a more complicated program than this one; we will now change this so that `Loop` gain ownership over the content.
Of course, we should make sure they are properly deallocated at destruction.
New main will be:
%% Cell type:code id: tags:
``` C++17
int main(int argc, char** argv)
{
static_cast<void>(argc); // to silence warning about unused argc - don't bother
static_cast<void>(argv); // to silence warning about unused argv - don't bother
Loop loop(3);
loop.Register(new TestDisplayPowerOfTwoApprox065(1000000));
loop.Register(new TestDisplayPowerOfTwoApprox035(1000000));
loop.Register(new TestDisplaySum(1000000));
loop.Do(4, 16, 4);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
### EXERCICE 30: transform `Loop` into `TestDisplayContainer`
Once again we probably went a bridge too far: it is useful to provide an object which contains several `TestDisplay` together, but making it take in charge the loop might not be that good an idea (in a real program you might for instance interact with this container by another mean than the pre-defined loop).
We will therefore rename the class `Loop` `TestDisplayContainer`, and reintroduce a `loop()` function that will interact with it. The `main()` is now:
%% Cell type:code id: tags:
``` C++17
int main(int argc, char** argv)
{
static_cast<void>(argc); // to silence warning about unused argc - don't bother
static_cast<void>(argv); // to silence warning about unused argv - don't bother
TestDisplayContainer container(3);
container.Register(new TestDisplayPowerOfTwoApprox065(1000000));
container.Register(new TestDisplayPowerOfTwoApprox035(1000000));
container.Register(new TestDisplaySum(1000000));
loop(4, 16, 4, container);
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
We will need to define two methods for `TestDisplayContainer`:
* A method `Nelement()` which returns the number of elements.
* A method `GetElement(int i)` which returns the i-th element (and provide an error if i is invalid).
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared and redacted by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been redacted by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment