Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 0e4df27f authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Fix two errors in object programming TP.

parent a47f8cf7
No related branches found
No related tags found
No related merge requests found
%% 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.
* Increment over the number of bits
* 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.
Replace `loop()` by a class `Loop` with a method `Do()`.
%% 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);
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)_
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment