Commit d933f272 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Exercices about templates have been refactored (and greatly reduced: all the...

Exercices about templates have been refactored (and greatly reduced: all the error handling will be tacked in the next notebook).
parent 988cce0f
%% 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)
* [TP 12](/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)
* [TP 12](/notebooks/4-Templates/3b-TP.ipynb)
* [TP 13](/notebooks/4-Templates/3b-TP.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-2019_
_This notebook is an adaptation of a lecture prepared 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 written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
This diff is collapsed.
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [TP 12](/notebooks/4-Templates/3b-TP.ipynb)
# [Getting started in C++](/) - [Templates](/notebooks/4-Templates/0-main.ipynb) - [TP 13](/notebooks/4-Templates/3b-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-42:-make-TestDisplayPowerOfTwoApprox-a-template-class" data-toc-modified-id="EXERCICE-42:-make-TestDisplayPowerOfTwoApprox-a-template-class-1">EXERCICE 42: make <code>TestDisplayPowerOfTwoApprox</code> a template class</a></span></li><li><span><a href="#[optional]-EXERCICE-43:-char-display" data-toc-modified-id="[optional]-EXERCICE-43:-char-display-2">[optional] EXERCICE 43: <code>char</code> display</a></span></li><li><span><a href="#EXERCICE-44:-integer-template-argument" data-toc-modified-id="EXERCICE-44:-integer-template-argument-3">EXERCICE 44: integer template argument</a></span></li></ul></div>
<div class="toc"><ul class="toc-item"><li><span><a href="#EXERCICE-33:-make-TestDisplayPowerOfTwoApprox-a-template-class" data-toc-modified-id="EXERCICE-33:-make-TestDisplayPowerOfTwoApprox-a-template-class-1">EXERCICE 33: make <code>TestDisplayPowerOfTwoApprox</code> a template class</a></span></li><li><span><a href="#EXERCICE-34:-integer-template-argument" data-toc-modified-id="EXERCICE-34:-integer-template-argument-2">EXERCICE 34: integer template argument</a></span></li></ul></div>
%% Cell type:markdown id: tags:
### EXERCICE 42: make `TestDisplayPowerOfTwoApprox` a template class
### EXERCICE 33: make `TestDisplayPowerOfTwoApprox` a template class
Now we will make `TestDisplayPowerOfTwoApprox` (and therefore its derived classes) a template class. Don't forget to modify `TestDisplayPowerOfTwoApprox<IntT>::Display()` and make the local `PowerOfTwoApprox` object the templated type!
Now we will make `TestDisplayPowerOfTwoApprox` (and therefore its derived classes) a template class. We will therefore remove the hardcoded `long` we introduced in previous exercice.
In the derived classes, you will need to help the compiler to figure out inheritance is at play, with `this` or an appropriate alias.
Try it with the following `main()`:
````
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<int>(1000000));
container.Register(new TestDisplayPowerOfTwoApprox035<int>(1000000));
container.Register(new TestDisplaySum<int>(1000000));
loop(4, 16, 4, container);
return EXIT_SUCCESS;
}
````
You should get:
````
[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:
### [optional] EXERCICE 43: `char` display
I'm not sure you would be willing to try it given the work `short` already gave, but you may use an even shorter type to represent an integer: `char`!
Replace the `main` by the following:
%% Cell type:code id: tags:
``` C++17
// Not executable as a Xeus-cling cell!
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(4);
container.Register(new TestDisplayPowerOfTwoApprox065<int>(1000000));
container.Register(new TestDisplayPowerOfTwoApprox035<int>(1000000));
container.Register(new TestDisplaySum<int>(1000000));
loop(4, 16, 4, container);
}
{
TestDisplayContainer container(2);
container.Register(new TestDisplayPowerOfTwoApprox065<char>(1000));
container.Register(new TestDisplayPowerOfTwoApprox065<unsigned char>(1000));
loop(1, 8, 1, container);
}
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
You should observe gibberish like:
````
Overflow in times_power_of_2 with number = @ and exponent = 1
````
Patch the outputs so that char integers are properly printed as numbers.
Expected should result the same... but now the end-user may modify at one place the type if he wants to try with another type.
%% Cell type:markdown id: tags:
### EXERCICE 44: integer template argument
### EXERCICE 34: integer template argument
Make `TestDisplayContainer` a template class which template argument is an integer `SizeN` that specifies the number of elements that might be stored inside (data attribute `size_` is of course no longer required!)
Make `TestDisplayContainer` a template class which template argument is an integer `CapacityN` that specifies the number of elements that might be stored inside (data attribute `capacity_` is of course no longer required!)
There are several school of thoughts about the type you should give this integer:
* The inspiration for this lecture advised strongly to make it an `int`, to avoid conversions when the size was compared with other quantities that are represented by an `int`. You should in this case add a `static_assert` within the class declaration to ensure the quantity is positive:
````
static_assert(SizeN > 0)
````
* You may prefer to use an `unsigned int` - personally I like to use this type when a quantity has no business being negative.
* You may prefer to use an `unsigned int` - personally I like to use unsigned types when a quantity has no business being negative.
* You may even prefer `std::size_t`, which is for more distribution an alias for `unsigned long`. This might seem overkill, but it is the choice in the standard library containers.
In the proposed solution I will choose the first solution to illustrate `static_assert`, but in a true project I would probably take the third one.
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared 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 written 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()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Debug' as none was specified.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()
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.")
include(../../cmake/Settings.cmake)
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")
include(../../cmake/AfterProjectSettings.cmake)
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)
#add_executable(exercice41 exercice41.cpp)
#add_executable(exercice42 exercice42.cpp)
#add_executable(exercice43 exercice43.cpp)
#add_executable(exercice44 exercice44.cpp)
\ No newline at end of file
# add_executable(exercice31 exercice31.cpp)
# add_executable(exercice32 exercice32.cpp)
# add_executable(exercice33 exercice33.cpp)
# add_executable(exercice34 exercice34.cpp)
\ No newline at end of file
#include <iostream>
#include <sstream> // for std::ostringstream
#include <string>
#include <cmath> // for std::round
//! Returns `number` * (2 ^ `exponent`)
template<class IntT>
IntT times_power_of_2(IntT number, int exponent)
{
constexpr IntT two = static_cast<IntT>(2);
while (exponent > 0)
{
number *= two;
exponent -= 1;
}
while (exponent < 0)
{
number /= two;
exponent += 1 ;
}
return number;
}
//! Round to `x` the nearest integer.
template<class IntT>
IntT round_as_int(double x)
{
return static_cast<IntT>(std::round(x));
}
// Maximum integer that might be represented with `Nbits` bits.
template<class IntT>
IntT max_int(int Nbits)
{
constexpr IntT one = static_cast<IntT>(1);
return (times_power_of_2(one, Nbits) - one);
}
//! Class to group the two integers used in the approximation we define.
template<class IntT>
class PowerOfTwoApprox
{
public:
//! Compute the best possible approximation of `value` with `Nbits`
PowerOfTwoApprox(int Nbits, double value);
//! \return The approximation as a floating point.
explicit operator double() const;
//! Accessor to numerator.
IntT GetNumerator() const;
//! Accessor to exponent.
int GetExponent() const;
private:
IntT numerator_ {};
int exponent_ {};
};
template<class IntT>
std::ostream& operator<<(std::ostream& out, const PowerOfTwoApprox<IntT>& approximation)
{
out << approximation.GetNumerator() << "/2^" << approximation.GetExponent();
return out;
}
/*!
* \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 IntT>
IntT operator*(IntT coefficient, const PowerOfTwoApprox<IntT>& approx)
{
return times_power_of_2(approx.GetNumerator() * coefficient, -approx.GetExponent());
}
template<class IntT>
IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)
{
return coefficient * approx;
}
//! Helper function that computes numerator and denominator.
//! You're not obliged to use a function, but this way you enforce the Don't Repeat Yourself (DRY) principle!
//! Note: could be put in the struct... but may be kept a free function as well! We will see much later
//! the anonymous namespace, in which I would typically put such a free function.
template<class IntT>
void helper_compute_power_of_2_approx(double value, int exponent, IntT& numerator, IntT& denominator)
{
constexpr IntT one = static_cast<IntT>(1);
denominator = times_power_of_2(one, exponent);
numerator = round_as_int<IntT>(value * denominator);
}
template<class IntT>
PowerOfTwoApprox<IntT>::PowerOfTwoApprox(int Nbits, double value)
{
IntT max_numerator = max_int<IntT>(Nbits);
auto& numerator = numerator_;
int& exponent = exponent_;
numerator = 0;
exponent = 0;
IntT denominator {};
do
{
// I used here the preffix increment '++exponent' but you may put it on a separate line if you're not
// comfortable with it.
helper_compute_power_of_2_approx(value, ++exponent, numerator, denominator);
}
while (numerator <= max_numerator);
helper_compute_power_of_2_approx(value, --exponent, numerator, denominator);
}
template<class IntT>
PowerOfTwoApprox<IntT>::operator double() const
{
constexpr IntT one = static_cast<IntT>(1);
IntT denominator = times_power_of_2(one, exponent_);
return static_cast<double>(numerator_) / denominator;
}
template<class IntT>
IntT PowerOfTwoApprox<IntT>::GetNumerator() const
{
return numerator_;
}
template<class IntT>
int PowerOfTwoApprox<IntT>::GetExponent() const
{
return exponent_;
}
enum class RoundToInteger { no, yes };
class TestDisplay
{
public:
//! Constructor which sets the only the resolution (i.e. the maximum index upon which error is defined).
TestDisplay(int resolution);
//! To make TestDisplay an abstract class.
virtual ~TestDisplay();
//! Pure virtual method Do().
virtual void operator()(int Nbits) const = 0;
protected:
/*!
* \brief Print a line with information about error.
*
* \param[in] optional_string1 String that might appear just after the "[With N bits]:".
* \param[in] optional_string2 String that might appear just before the "[error = ...]".
* \param[in] do_round_to_integer If yes, the exact result is approximated as an integer.
*/
template<class IntT>
void PrintLine(int Nbits, double exact, double approx,
std::string optional_string1 = "", std::string optional_string2 = "",
RoundToInteger do_round_to_integer = RoundToInteger::no) const;
private:
//! Resolution.
const int resolution_;
};
TestDisplay::TestDisplay(int resolution)
: resolution_(resolution)
{ }
TestDisplay::~TestDisplay() = default;
void TestDisplay::operator()(int Nbits) const
{
static_cast<void>(Nbits); // neutralize warning about unused argument at no runtime cost.
}
template<class IntT>
void TestDisplay::PrintLine(int Nbits, double exact, double approx,
std::string optional_string1, std::string optional_string2,
RoundToInteger do_round_to_integer) const
{
IntT error = round_as_int<IntT>(resolution_ * std::fabs(exact - approx) / exact);
std::cout << "[With " << Nbits << " bits]: " << optional_string1
<< (do_round_to_integer == RoundToInteger::yes ? round_as_int<IntT>(exact) : exact) << " ~ " << approx
<< optional_string2
<< " [error = " << error << "/" << resolution_ << "]"
<< std::endl;
}
template<class IntT>
class TestDisplayPowerOfTwoApprox : public TestDisplay
{
public:
//! Constructor.
TestDisplayPowerOfTwoApprox(int resolution);
//! Destructor
virtual ~TestDisplayPowerOfTwoApprox() override;
//! Pure virtual method Do().
virtual void operator()(int Nbits)const override = 0;
protected:
//! Method in charge of the actual display.
void Display(int Nbits, double value) const;
};
template<class IntT>
TestDisplayPowerOfTwoApprox<IntT>::TestDisplayPowerOfTwoApprox(int resolution)
: TestDisplay(resolution)
{ }
template<class IntT>
TestDisplayPowerOfTwoApprox<IntT>::~TestDisplayPowerOfTwoApprox() = default;
template<class IntT>
void TestDisplayPowerOfTwoApprox<IntT>::Display(int Nbits, double value) const
{
PowerOfTwoApprox<IntT> approximation(Nbits, value);
const double approx = static_cast<double>(approximation);
std::ostringstream oconv;
oconv << " (" << approximation << ")";
PrintLine<IntT>(Nbits, value, approx, "", oconv.str());
}
template<class IntT>
class TestDisplayPowerOfTwoApprox065 : public TestDisplayPowerOfTwoApprox<IntT>
{
public:
//! Constructor.
TestDisplayPowerOfTwoApprox065(int resolution);
//! Destructor,
~TestDisplayPowerOfTwoApprox065() override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
};
template<class IntT>
TestDisplayPowerOfTwoApprox065<IntT>::TestDisplayPowerOfTwoApprox065(int resolution)
: TestDisplayPowerOfTwoApprox<IntT>(resolution)
{ }
template<class IntT>
TestDisplayPowerOfTwoApprox065<IntT>::~TestDisplayPowerOfTwoApprox065() = default;
template<class IntT>
void TestDisplayPowerOfTwoApprox065<IntT>::operator()(int Nbits) const
{
this->Display(Nbits, 0.65);
}
template<class IntT>
class TestDisplayPowerOfTwoApprox035 : public TestDisplayPowerOfTwoApprox<IntT>
{
public:
//! Constructor.
TestDisplayPowerOfTwoApprox035(int resolution);
//! Destructor,
~TestDisplayPowerOfTwoApprox035() override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
};
template<class IntT>
TestDisplayPowerOfTwoApprox035<IntT>::TestDisplayPowerOfTwoApprox035(int resolution)
: TestDisplayPowerOfTwoApprox<IntT>(resolution)
{ }
template<class IntT>
TestDisplayPowerOfTwoApprox035<IntT>::~TestDisplayPowerOfTwoApprox035() = default;
template<class IntT>
void TestDisplayPowerOfTwoApprox035<IntT>::operator()(int Nbits) const
{
this->Display(Nbits, 0.35);
}
template<class IntT>
class TestDisplayMultiply : public TestDisplay
{
public:
//! Constructor.
explicit TestDisplayMultiply(int resolution);
//! To make the class a concrete one.
virtual ~TestDisplayMultiply() override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
private:
//! Method in charge of the actual display.
void Display(int Nbits, double value1, IntT coefficient1, double value2, IntT coefficient2) const;
};
template<class IntT>
TestDisplayMultiply<IntT>::TestDisplayMultiply(int resolution)
: TestDisplay(resolution)
{ }
template<class IntT>
TestDisplayMultiply<IntT>::~TestDisplayMultiply() = default;
template<class IntT>
void TestDisplayMultiply<IntT>::operator()(int Nbits) const
{
Display(Nbits, 0.65, static_cast<IntT>(3515), 0.35, static_cast<IntT>(4832));
}
template<class IntT>
void TestDisplayMultiply<IntT>::Display(int Nbits, double value1, IntT coefficient1, double value2, IntT coefficient2) const
{
double exact = value1 * coefficient1 + value2 * coefficient2;
PowerOfTwoApprox<IntT> approximation1(Nbits, value1);
PowerOfTwoApprox<IntT> approximation2(Nbits, value2);
IntT approx = approximation1 * coefficient1 + coefficient2 * approximation2;