Commit 2f058e46 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

TP template in progress.

parent 3d2bb200
......@@ -12,6 +12,7 @@
"metadata": {},
"source": [
"* [Introduction to the concept of templates](/notebooks/4-Templates/1-Intro.ipynb)\n",
" * [TP 11](/notebooks/4-Templates/1b-TP.ipynb)\n",
"* [Specialization](/notebooks/4-Templates/2-Specialization.ipynb)\n",
"* [Special syntax: typename, template and mandatory this](/notebooks/4-Templates/3-Syntax.ipynb)\n",
"* [Metaprogramming](/notebooks/4-Templates/4-Metaprogramming.ipynb)\n",
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb)
%% Cell type:markdown id: tags:
* [Introduction to the concept of templates](/notebooks/4-Templates/1-Intro.ipynb)
* [TP 11](/notebooks/4-Templates/1b-TP.ipynb)
* [Specialization](/notebooks/4-Templates/2-Specialization.ipynb)
* [Special syntax: typename, template and mandatory this](/notebooks/4-Templates/3-Syntax.ipynb)
* [Metaprogramming](/notebooks/4-Templates/4-Metaprogramming.ipynb)
* [Hints to more advanced concepts with templates](/notebooks/4-Templates/5-MoreAdvanced.ipynb)
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
......
......@@ -717,13 +717,6 @@
"_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/)_ \n",
"_The present version has been redacted by Sébastien Gilles and Vincent Rouvreau (Inria)_"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [Introduction](/notebooks/4-Templates/1-Intro.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="#Motivation" data-toc-modified-id="Motivation-1">Motivation</a></span></li><li><span><a href="#Function-templates-(or-methods)" data-toc-modified-id="Function-templates-(or-methods)-2">Function templates (or methods)</a></span><ul class="toc-item"><li><span><a href="#static_assert" data-toc-modified-id="static_assert-2.1"><code>static_assert</code></a></span></li></ul></li><li><span><a href="#Class-templates" data-toc-modified-id="Class-templates-3">Class templates</a></span><ul class="toc-item"><li><span><a href="#Template-method-of-a-template-class" data-toc-modified-id="Template-method-of-a-template-class-3.1">Template method of a template class</a></span></li></ul></li><li><span><a href="#Type-or-non-type-template-parameter" data-toc-modified-id="Type-or-non-type-template-parameter-4">Type or non-type template parameter</a></span></li><li><span><a href="#Few-precisions-about-templates" data-toc-modified-id="Few-precisions-about-templates-5">Few precisions about templates</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Motivation
The typical introduction to the need of generic programming is implementing a function that determines the minimum between two quantities (by the way don't do that: STL provides it already...)
%% Cell type:code id: tags:
``` C++17
int min(int lhs, int rhs)
{
return lhs < rhs ? lhs : rhs;
}
```
%% Cell type:code id: tags:
``` C++17
double min(double lhs, double rhs)
{
return lhs < rhs ? lhs : rhs;
}
```
%% Cell type:code id: tags:
``` C++17
float min(float lhs, float rhs)
{
return lhs < rhs ? lhs : rhs;
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout << min(5, -8) << std::endl;
std::cout << min(5., -8.) << std::endl;
std::cout << min(5.f, -8.f) << std::endl;
}
```
%%%% Output: stream
-8
-8
-8
%% Cell type:markdown id: tags:
Already tired? Yet we have still to define the same for unsigned versions, other types such as `short` ou `long`... And it's not very [**DRY** (Don't Repeat Yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself): you may have noticed the implementation is exactly the same!
## Function templates (or methods)
A mechanism was therefore introduced in C++ to provide only one implementation: the **templates**:
%% Cell type:code id: tags:
``` C++17
template<class T>
T min2(T lhs, T rhs)
{
return lhs < rhs ? lhs : rhs;
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
std::cout << min2<int>(5, -8) << std::endl;
std::cout << min2(5, -8) << std::endl;
std::cout << min2(5., -8.) << std::endl;
std::cout << min2(5.f, -8.f) << std::endl;
}
```
%%%% Output: stream
-8
-8
-8
-8
%% Cell type:markdown id: tags:
As you can see:
* The type is replaced by a parameter (here called `T`)
* In the function call, you might specify the type explicitly between brackets (`<>`). If not specified, the compiler may figure it out if said type is used for one of the parameters. In other words, for the following case it won't work:
%% Cell type:code id: tags:
``` C++17
template<class T>
T Convert(int value)
{
return static_cast<T>(value);
}
```
%% Cell type:code id: tags:
``` C++17
{
Convert(5); // Error: can't figure out which type `T` to use!
}
```
%% Cell type:code id: tags:
``` C++17
{
Convert<double>(5); // Ok
}
```
%% Cell type:markdown id: tags:
You may have see noticed there are no constraints on `T` whatsoever. If you invoke a template and the implementation doesn't make sense for the chosen type the compiler will yell (no doubt you have already seen compilers are extremely talented to do so and it is even truer regarding templates...)
%% Cell type:code id: tags:
``` C++17
#include <string>
{
Convert<std::string>(5); // Doesn't make sense so compiler will yell!
}
```
%%%% Output: stream
input_line_16:3:5: error: use of undeclared identifier 'Convert'
Convert<std::string>(5); // Doesn't make sense so compiler will yell!
 ^
input_line_16:3:24: error: expected '(' for function-style cast or type construction
Convert<std::string>(5); // Doesn't make sense so compiler will yell!
 ~~~~~~~~~~~^

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
### `static_assert`
Of course, it would be nicer to get a clearer error message when an impossible type is provided... C++ 20 should introduce the [concept](https://en.cppreference.com/w/cpp/language/constraints) to constraint properly which type is acceptable, but C++ 11 already introduced a way slightly better than C++ 03 with `static_assert`:
%% Cell type:code id: tags:
``` C++17
#include <type_traits> // for std::is_arithmetic
template<class T>
T Convert2(int value)
{
static_assert(std::is_arithmetic<T>(), "T must be an integer or a floating point!");
return static_cast<T>(value);
}
```
%% Cell type:code id: tags:
``` C++17
#include <string>
{
Convert2<std::string>(5); // Doesn't make sense so compiler will yell!
// But first line is much clearer than previously...
}
```
%%%% Output: stream
input_line_18:4:5: error: static_assert failed "T must be an integer or a floating point!"
static_assert(std::is_arithmetic<T>(), "T must be an integer or a floating point!");
 ^ ~~~~~~~~~~~~~~~~~~~~~~~
input_line_20:3:5: note: in instantiation of function template specialization 'Convert2<std::__1::basic_string<char> >' requested here
Convert2<std::string>(5); // Doesn't make sense so compiler will yell!
 ^
input_line_18:5:12: error: no matching conversion for static_cast from 'int' to 'std::__1::basic_string<char>'
return static_cast<T>(value);
 ^~~~~~~~~~~~~~~~~~~~~
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:786:40: note: candidate constructor not viable: no known conversion from 'int' to 'const std::__1::basic_string<char,
std::__1::char_traits<char>, std::__1::allocator<char> >::allocator_type' (aka 'const std::__1::allocator<char>') for 1st
argument
_LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a)
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:793:5: note: candidate constructor not viable: no known conversion from 'int' to 'const std::__1::basic_string<char>' for 1st argument
basic_string(const basic_string& __str);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:798:5: note: candidate constructor not viable: no known conversion from 'int' to 'std::__1::basic_string<char>' for 1st argument
basic_string(basic_string&& __str)
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:811:5: note: candidate constructor not viable: no known conversion from 'int' to 'const char *' for 1st argument
basic_string(const _CharT* __s) {
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:861:5: note: candidate constructor not viable: no known conversion from 'int' to 'initializer_list<char>' for 1st argument
basic_string(initializer_list<_CharT> __il);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:847:18: note: candidate template ignored: requirement '__can_be_converted_to_string_view<char, char_traits<char>, int>::value' was not satisfied
[with _Tp = int]
explicit basic_string(const _Tp& __t);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:821:9: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
basic_string(const _CharT* __s, const _Allocator& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:832:9: note: candidate constructor template not viable: requires 3 arguments, but 1 was provided
basic_string(size_type __n, _CharT __c, const _Allocator& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:842:9: note: candidate constructor template not viable: requires at least 3 arguments, but 1 was provided
basic_string(const _Tp& __t, size_type __pos, size_type __n,
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:851:18: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
explicit basic_string(const _Tp& __t, const allocator_type& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:855:9: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
basic_string(_InputIterator __first, _InputIterator __last);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:858:9: note: candidate constructor template not viable: requires 3 arguments, but 1 was provided
basic_string(_InputIterator __first, _InputIterator __last, const allocator_type& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:783:31: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
_LIBCPP_INLINE_VISIBILITY basic_string()
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:794:5: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
basic_string(const basic_string& __str, const allocator_type& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:806:5: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
basic_string(basic_string&& __str, const allocator_type& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:824:5: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
basic_string(const _CharT* __s, size_type __n);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:828:5: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
basic_string(size_type __n, _CharT __c);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:863:5: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
basic_string(initializer_list<_CharT> __il, const _Allocator& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:826:5: note: candidate constructor not viable: requires 3 arguments, but 1 was provided
basic_string(const _CharT* __s, size_type __n, const _Allocator& __a);
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:837:5: note: candidate constructor not viable: requires at least 2 arguments, but 1 was provided
basic_string(const basic_string& __str, size_type __pos,
 ^
/Users/Shared/Software/miniconda3/envs/cling/include/c++/v1/string:834:5: note: candidate constructor not viable: requires at least 3 arguments, but 1 was provided
basic_string(const basic_string& __str, size_type __pos, size_type __n,
 ^

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
`static_assert` evolved in C++ 14:
* In C++ 11, it takes two arguments: the test and a string which features an explanation on the topic at hand.
* In C++ 14 and above, you might just give the test; it is actually handy when the test is already crystal clear:
````static_assert("std::is_same<T, int>(), "Check T is an integer");```` is a tad overkill!
That being said, if the test is not that trivial you should really use the possibility to add an explanation.
## Class templates
We have seen templates in the case of functions, but classes can be templated as well:
%% Cell type:code id: tags:
``` C++17
template<class T>
class HoldAValue
{
public:
HoldAValue(T value);
T GetValue() const;
private:
T value_;
};
```
%% Cell type:code id: tags:
``` C++17
template<class T>
HoldAValue<T>::HoldAValue(T value) // the <T> is mandatory to identify properly the class
: value_(value)
{ }
```
%% Cell type:code id: tags:
``` C++17
template<class T>
T HoldAValue<T>::GetValue() const
{
return value_;
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <string>
{
HoldAValue integer(5);
std::cout << "Integer hold: " << integer.GetValue() << std::endl;
HoldAValue<std::string> string("Hello world!"); // If type not specified explicitly it would have been char*...
std::cout << "String hold: " << string.GetValue() << std::endl;
}
```
%%%% Output: stream
Integer hold: 5
String hold: Hello world!
%% Cell type:markdown id: tags:
The template must be reminded in the definition as well; please notice before the `::` the brackets with the parameters *without their type*.
### Template method of a template class
Notice a template class may provide template methods:
%% Cell type:code id: tags:
``` C++17
template<class T>
class HoldAValue2
{
public:
HoldAValue2(T value);
T GetValue() const;
template<class U>
U Convert() const;
private:
T value_;
};
```
%% Cell type:code id: tags:
``` C++17
template<class T>
HoldAValue2<T>::HoldAValue2(T value)
: value_(value)
{ }
```
%% Cell type:code id: tags:
``` C++17
template<class T>
T HoldAValue2<T>::GetValue() const
{
return value_;
}
```
%% Cell type:markdown id: tags:
In this case there are two `template` keyword for the definition: one for the class and the other for the method:
%% Cell type:code id: tags:
``` C++17
template<class T> // template type for the class first
template<class U> // then template type for the method
U HoldAValue2<T>::Convert() const
{
return static_cast<U>(value_);
}
```
%% Cell type:code id: tags:
``` C++17
{
HoldAValue2 hold(9);
hold.Convert<double>();
}
```
%% Cell type:markdown id: tags:
## Type or non-type template parameter
The examples so far are using **type** parameters: the `T` in the example stands for a type and is deemed to be substituted by a type. Templates may also use **non type** parameters, which are in most cases `enum` or `integral constant` types (beware: floating point types parameters or `std::string` **can't** be used as template parameters!)
Both can be mixed in a given template declaration:
%% Cell type:code id: tags:
``` C++17
template<class TypeT, std::size_t Nelts>
class MyArray
{
public:
explicit MyArray(TypeT initial_value);
private:
TypeT content_[Nelts];
};
```
%% Cell type:code id: tags:
``` C++17
template<class TypeT, std::size_t Nelts>
MyArray<TypeT, Nelts>::MyArray(TypeT initial_value)
{
for (auto i = 0ul; i < Nelts; ++i)
content_[i] = initial_value;
}
```
%% Cell type:code id: tags:
``` C++17
{
MyArray<int, 5ul> array1(2);
MyArray<double, 2ul> array2(3.3);
}
```
%% Cell type:markdown id: tags:
However, you can't provide a type parameter where a non-type is expected (and vice-versa):
%% Cell type:code id: tags:
``` C++17
{
MyArray<5ul, 5ul> array1(2); // COMPILATION ERROR!
}
```
%%%% Output: stream
input_line_34:3:13: error: template argument for template type parameter must be a type
MyArray<5ul, 5ul> array1(2);
 ^~~
input_line_31:1:16: note: template parameter is declared here
template<class TypeT, std::size_t Nelts>
 ^

%%%% Output: error
Interpreter Error:
%% Cell type:code id: tags:
``` C++17
{
MyArray<int, int> array1(2); // COMPILATION ERROR!
}
```
%%%% Output: stream
input_line_35:3:18: error: template argument for non-type template parameter must be an expression
MyArray<int, int> array1(2); // COMPILATION ERROR!
 ^~~
input_line_31:1:35: note: template parameter is declared here
template<class TypeT, std::size_t Nelts>
 ^

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
## Few precisions about templates
* Template parameters must be known or computed at **compile time**. You can't therefore instantiate a template from a value that was entered by the user of the program or computed at runtime.
* In the template syntax, `class` might be replaced by `typename`:
%% Cell type:code id: tags:
``` C++17
template<typename T>
T Convert3(int value)
{
return static_cast<T>(value);
}
```
%% Cell type:markdown id: tags:
There are exactly zero differences between both keywords; some authors suggest conventions (e.g. use `typename` for POD types and `class` for the more complicated types) but they are just that: conventions!
* The definition of the templates must be provided in header files, not in compiled files. The reason is that the compiler can't know all the types for which the template might be used: you may use `std::min` for your own defined types (provided they define a `<` operator...) and obviously STL writers can't foresee which type you will come up with!
* The compiler will generate the code for each type given to the template; for instance if `Convert<double>`, `Convert<unsigned int>` and `Convert<short>` are used in your code, the content of this template function will be instantiated thrice! This can lead to **code bloat**: lots of assembler code is generated, and compilation time may increase significatively.
* So templates are a _very_ nice tool, but make sure to use them only when they're relevant, and you may employ techniques to limit the amount of code actually defined within the template definitions.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
%% Cell type:code id: tags:
``` C++17
```
......
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Getting started in C++](/) - [Operators](/notebooks/4-Templates/0-main.ipynb) - [TP 11](/notebooks/4-Templates/1b-TP.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"toc": true
},
"source": [
"<h1>Table of contents<span class=\"tocSkip\"></span></h1>\n",
"<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Introduction\" data-toc-modified-id=\"Introduction-1\">Introduction</a></span></li><li><span><a href=\"#EXERCICE-37:-make-PowerOfTwoApprox-a-template-class\" data-toc-modified-id=\"EXERCICE-37:-make-PowerOfTwoApprox-a-template-class-2\">EXERCICE 37: make <code>PowerOfTwoApprox</code> a template class</a></span></li><li><span><a href=\"#EXERCICE-38:-consistency\" data-toc-modified-id=\"EXERCICE-38:-consistency-3\">EXERCICE 38: consistency</a></span></li><li><span><a href=\"#EXERCICE-39:-more-template-functions\" data-toc-modified-id=\"EXERCICE-39:-more-template-functions-4\">EXERCICE 39: more template functions</a></span></li></ul></div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Introduction\n",
"\n",
"[This notebook](/notebooks/TP/HowTo.ipynb) explains very briefly your options to run the TP.\n",
"\n",
"We will start from the solution to exercice 36 of the procedural object TP. `initial_file.cpp` in the TP directory is a copy to the solution of this exercice (symbolic link wouldn't have worked with Docker)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 37: make `PowerOfTwoApprox` a template class\n",
"\n",
"The integer type used for the numerator_ attribute of the `PowerOfTwoApprox` class was chosen arbitrarily. Make it a class template parameter.\n",
"\n",
"Only the numerator will be modified: the denominator and the number of bits remain `int`.\n",
"\n",
"Make sure to modify:\n",
"\n",
"* The type of the data attribute.\n",
"* The type returned by the `Numerator()` method.\n",
"\n",
"In the `TestDisplay` classes, we will for the moment keep instantiating the version with `int`.\n",
"\n",
"**IMPORTANT:** For the friendship declaration in `PowerOfTwoApprox` to `operator*`, you need to specify another template for the function: you can't use the same as the class one:\n",
"\n",
"````\n",
"template<class T>\n",
"class Foo\n",
"{\n",
"\n",
" friend void MyFunction<Foo<T>>; // doesn't do what you really want!\n",
"\n",
"};\n",
"````\n",
"\n",
"````\n",
"template<class T>\n",
"class Foo\n",
"{\n",
"\n",
" template<class U>\n",
" friend void MyFunction<Foo<U>>; // Ok!\n",
"\n",
"};\n",
"````\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 38: consistency\n",
"\n",
"So far, we aren't very self-consistent: we loosen the constraint on the type used to store the numerator, but it is used up against `int` elsewhere. \n",
"\n",
"To highlight this, change `PowerOfTwoApprox` instantiation by replacing `int` by `short`: you should see warnings pop up...\n",
"\n",
"To do so properly, you will have to make some classes and functions template. As you will see, doing such a change is rather cumbersome: it is better whenever possible to identify as soon as possible whether a template parameter is required or not.\n",
"\n",
"We will do so step by step, each exercice dealing with part of the changes.\n",
"\n",
"In the first step, we will:\n",
"\n",
"* Modify `operator*` signature so that the return type and the argument use the template parameter rather than `int`.\n",
"* Make `TestDisplaySum` a template class as well: as it uses up directly `operator*`, you have to do it now.\n",
"\n",
"You should see warnings to solve... and disturbing runtime effects: the computation gets stuck (we were not very aware so far of overflow issues and we end up here with infinite or at least very long loops...)\n",
"\n",
"Don't get me wrong: the issue is not the templates themselves, but the fact our code is not robust enough and that we didn't see it previously as we worked on types and values (no more than 16 bits...) for which the potentiel problems were not appearant."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 39: more template functions\n",
"\n",
"We will first heed the warnings: there is a type inconsistency due to the fact this function expects `int` and is fed `short`. Make some functions template ones to fix some of the warnings: `times_power_of_2`, `max_int` and `round_as_int`.\n",
"\n",
"**WARNING:** You may have a hard time fixing the one concerning `operator*` due to a pesky \n",
"\n",
"````\n",
" warning: implicit conversion loses\n",
" integer precision: 'int' to 'short' [-Wconversion]\n",
" return times_power_of_2(approx.Numerator() * coefficient, -approx.Exponent());\n",
"````\n",
"\n",
"This is due to a specificity I was not aware of before preparing this very TP: an arithmetic operator involving two `short` actually returns an `int`! For more of this, see this [StackOverflow question](https://stackoverflow.com/questions/24371868/why-must-a-short-be-converted-to-an-int-before-arithmetic-operations-in-c-and-c). I actually wasn't aware of this because I never use `short`: I was told long ago it was often a pessimization compared to a mere `int` and didn't dig deeper than that; the issue we met here seems to confirm it is quirky to use.\n",
"\n",
"One can fix the warning with an additional `static_cast`:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[1minput_line_7:2:22: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mno template named 'PowerOfTwoApprox'\u001b[0m\n",
"IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)\n",
"\u001b[0;1;32m ^\n",
"\u001b[0m"
]
},
{
"ename": "Interpreter Error",
"evalue": "",
"output_type": "error",
"traceback": [
"Interpreter Error: "
]
}
],
"source": [
"// Doesn't run in Xeus-cling\n",
"\n",
"template<class IntT>\n",
"IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)\n",
"{\n",
" // static_cast to deal with the `short` case\n",
" return static_cast<IntT>(times_power_of_2(approx.Numerator() * coefficient, -approx.Exponent()));\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 40: make `TestDisplayPowerOfTwoApprox` a template class\n",
"\n",
"PROBABLY IN NEXT TP WITH THIS!\n",
"\n",
"We have not fixed our precision issue: with what was done in exercice 39 `short` should be ok, but `unsigned short` for instance is still not correct.\n",
"\n",
"We will leave that to another TP and focus more on templates:\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"© _CNRS 2016_ - _Inria 2018_ \n",
"_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/)_ \n",
"_The present version has been redacted by Sébastien Gilles and Vincent Rouvreau (Inria)_"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "C++17",
"language": "C++17",
"name": "xeus-cling-cpp17"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".cpp",
"mimetype": "text/x-c++src",
"name": "c++",
"version": "-std=c++17"
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autoclose": false,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": false,
"sideBar": true,
"skip_h1_title": true,
"title_cell": "Table of contents",
"title_sidebar": "Contents",
"toc_cell": true,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
}
},
"nbformat": 4,
"nbformat_minor": 2
}
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Operators](/notebooks/4-Templates/0-main.ipynb) - [TP 11](/notebooks/4-Templates/1b-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="#Introduction" data-toc-modified-id="Introduction-1">Introduction</a></span></li><li><span><a href="#EXERCICE-37:-make-PowerOfTwoApprox-a-template-class" data-toc-modified-id="EXERCICE-37:-make-PowerOfTwoApprox-a-template-class-2">EXERCICE 37: make <code>PowerOfTwoApprox</code> a template class</a></span></li><li><span><a href="#EXERCICE-38:-consistency" data-toc-modified-id="EXERCICE-38:-consistency-3">EXERCICE 38: consistency</a></span></li><li><span><a href="#EXERCICE-39:-more-template-functions" data-toc-modified-id="EXERCICE-39:-more-template-functions-4">EXERCICE 39: more template functions</a></span></li></ul></div>
%% Cell type:markdown id: tags:
### Introduction
[This notebook](/notebooks/TP/HowTo.ipynb) explains very briefly your options to run the TP.
We will start from the solution to exercice 36 of the procedural object TP. `initial_file.cpp` in the TP directory is a copy to the solution of this exercice (symbolic link wouldn't have worked with Docker).
%% Cell type:markdown id: tags:
### EXERCICE 37: make `PowerOfTwoApprox` a template class
The integer type used for the numerator_ attribute of the `PowerOfTwoApprox` class was chosen arbitrarily. Make it a class template parameter.
Only the numerator will be modified: the denominator and the number of bits remain `int`.
Make sure to modify:
* The type of the data attribute.
* The type returned by the `Numerator()` method.
In the `TestDisplay` classes, we will for the moment keep instantiating the version with `int`.
**IMPORTANT:** For the friendship declaration in `PowerOfTwoApprox` to `operator*`, you need to specify another template for the function: you can't use the same as the class one:
````
template<class T>
class Foo
{
friend void MyFunction<Foo<T>>; // doesn't do what you really want!
};
````
````
template<class T>
class Foo
{
template<class U>
friend void MyFunction<Foo<U>>; // Ok!
};
````
%% Cell type:markdown id: tags:
### EXERCICE 38: consistency
So far, we aren't very self-consistent: we loosen the constraint on the type used to store the numerator, but it is used up against `int` elsewhere.
To highlight this, change `PowerOfTwoApprox` instantiation by replacing `int` by `short`: you should see warnings pop up...
To do so properly, you will have to make some classes and functions template. As you will see, doing such a change is rather cumbersome: it is better whenever possible to identify as soon as possible whether a template parameter is required or not.
We will do so step by step, each exercice dealing with part of the changes.
In the first step, we will:
* Modify `operator*` signature so that the return type and the argument use the template parameter rather than `int`.
* Make `TestDisplaySum` a template class as well: as it uses up directly `operator*`, you have to do it now.
You should see warnings to solve... and disturbing runtime effects: the computation gets stuck (we were not very aware so far of overflow issues and we end up here with infinite or at least very long loops...)
Don't get me wrong: the issue is not the templates themselves, but the fact our code is not robust enough and that we didn't see it previously as we worked on types and values (no more than 16 bits...) for which the potentiel problems were not appearant.
%% Cell type:markdown id: tags:
### EXERCICE 39: more template functions
We will first heed the warnings: there is a type inconsistency due to the fact this function expects `int` and is fed `short`. Make some functions template ones to fix some of the warnings: `times_power_of_2`, `max_int` and `round_as_int`.
**WARNING:** You may have a hard time fixing the one concerning `operator*` due to a pesky
````
warning: implicit conversion loses
integer precision: 'int' to 'short' [-Wconversion]
return times_power_of_2(approx.Numerator() * coefficient, -approx.Exponent());
````
This is due to a specificity I was not aware of before preparing this very TP: an arithmetic operator involving two `short` actually returns an `int`! For more of this, see this [StackOverflow question](https://stackoverflow.com/questions/24371868/why-must-a-short-be-converted-to-an-int-before-arithmetic-operations-in-c-and-c). I actually wasn't aware of this because I never use `short`: I was told long ago it was often a pessimization compared to a mere `int` and didn't dig deeper than that; the issue we met here seems to confirm it is quirky to use.
One can fix the warning with an additional `static_cast`:
%% Cell type:code id: tags:
``` C++17
// Doesn't run in Xeus-cling
template<class IntT>
IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)
{
// static_cast to deal with the `short` case
return static_cast<IntT>(times_power_of_2(approx.Numerator() * coefficient, -approx.Exponent()));
}
```
%%%% Output: stream
input_line_7:2:22: error: no template named 'PowerOfTwoApprox'
IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)
 ^

%%%% Output: error
Interpreter Error:
%% Cell type:markdown id: tags:
### EXERCICE 40: make `TestDisplayPowerOfTwoApprox` a template class
PROBABLY IN NEXT TP WITH THIS!
We have not fixed our precision issue: with what was done in exercice 39 `short` should be ok, but `unsigned short` for instance is still not correct.
We will leave that to another TP and focus more on templates:
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018_
_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)_
cmake_minimum_required(VERSION 3.9)
# Add a compiler flag only if it is accepted.
# This macro is usually defined once and for all in a specific file which is included in the CMakeLists.txt in which
# you need it.
include(CheckCXXCompilerFlag)
macro(add_cxx_compiler_flag _flag)
string(REPLACE "-" "_" _flag_var ${_flag})
check_cxx_compiler_flag("${_flag}" CXX_COMPILER_${_flag_var}_OK)
if(CXX_COMPILER_${_flag_var}_OK)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_flag}")
endif()
endmacro()
set(CMAKE_CXX_COMPILER clang++ CACHE STRING "C++ compiler")
set(CMAKE_C_COMPILER clang CACHE STRING "C compiler")
set(CMAKE_CXX_STANDARD 17 CACHE INTEGER "Version of the C++ standard to use")
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "If ON the GNU version of the standard is used.")
project(GettingStartedWithModernCpp_TP_Template)
add_cxx_compiler_flag("-Weverything") # all warnings for clang
add_cxx_compiler_flag("-Wall") # for gcc (and recognized by clang)
add_cxx_compiler_flag("-Wextra") # for gcc (and recognized by clang)
add_cxx_compiler_flag("-Wconversion")# for gcc
add_cxx_compiler_flag("-Wno-c++98-compat") # We assume we are using modern C++
add_cxx_compiler_flag("-Wno-c++98-compat-pedantic") # We assume we are using modern C++
add_cxx_compiler_flag("-Wno-padded")
add_executable(initial initial_file.cpp)
add_executable(exercice37 exercice37.cpp)
add_executable(exercice38 exercice38.cpp)
add_executable(exercice39 exercice39.cpp)
#add_executable(exercice40 exercice40.cpp)
\ No newline at end of file
#include <iostream>
#include <cmath> // for std::round
#include <string>
#include <sstream> // for std::ostringstream
/************************************/
// Declarations
/************************************/
/*!
* \brief Structure to group together numerator and exponent used to represent a floating-point.
*
* Representation is numerator / 2^exponent, where numerator is as large as a selected number of bits make possible.
*/
template<class IntT>
class PowerOfTwoApprox
{
public:
//! Constructor that compute the best possible approximation of `value` with `Nbits`
PowerOfTwoApprox(int Nbits, double value);
//! Returns the double value computed from `numerator_` and `exponent_`.
explicit operator double() const;
/*!
* \brief Multiply the approximate representation by an integer.
*
* \param[in] coefficient Integer coefficient by which the object is multiplied.
*
* \return An approximate integer result of the multiplication.
*/
template<class IntU>
friend int operator*(const PowerOfTwoApprox<IntU>& approx, int coefficient);
template<class IntU>
friend int operator*(int coefficient, const PowerOfTwoApprox<IntU>& approx);
//! Get the value of the numerator.
IntT Numerator() const;
//! Get the value of the exponent.
int Exponent() const;
private:
//! Numerator.
IntT numerator_ = 0;
//! Exponent applied to the 2 at the denominator.
int exponent_ = 0;
};
namespace std
{
template<class IntT>
std::ostream& operator<<(std::ostream& out, const PowerOfTwoApprox<IntT>& approx);
} // namespace std
/*!
* \brief Base class for TestDisplayPowerOfTwoApprox and TestDisplaySum.
*/
class TestDisplay
{
public:
//! Constructor.
TestDisplay(int resolution);
//! Virtual method.
virtual void operator()(int Nbits) const = 0;
//! Virtual destructor.
virtual ~TestDisplay();
protected:
/*!
* \brief Print a line with information about error.