Attention une mise à jour du service Gitlab va être effectuée le mardi 30 novembre entre 17h30 et 18h00. Cette mise à jour va générer une interruption du service dont nous ne maîtrisons pas complètement la durée mais qui ne devrait pas excéder quelques minutes. Cette mise à jour intermédiaire en version 14.0.12 nous permettra de rapidement pouvoir mettre à votre disposition une version plus récente.

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

Revamp last TP.

parent 0400e7fe
......@@ -18,9 +18,8 @@
" * [TP 15](/notebooks/5-UsefulConceptsAndSTL/3b-TP.ipynb)\n",
"* [Associative containers](/notebooks/5-UsefulConceptsAndSTL/4-AssociativeContainers.ipynb)\n",
"* [Move semantics](/notebooks/5-UsefulConceptsAndSTL/5-MoveSemantics.ipynb)\n",
" * [TP 16](/notebooks/5-UsefulConceptsAndSTL/5b-TP.ipynb)\n",
"* [Smart pointers](/notebooks/5-UsefulConceptsAndSTL/6-SmartPointers.ipynb)\n",
" * [TP 17](/notebooks/5-UsefulConceptsAndSTL/6b-TP.ipynb)\n",
" * [TP 16](/notebooks/5-UsefulConceptsAndSTL/6b-TP.ipynb)\n",
"* [Algorithms](/notebooks/5-UsefulConceptsAndSTL/7-Algorithms.ipynb)\n"
]
},
......
......@@ -14,7 +14,7 @@
},
"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-52\" data-toc-modified-id=\"EXERCICE-52-2\">EXERCICE 52</a></span></li></ul></div>"
"<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-43\" data-toc-modified-id=\"EXERCICE-43-2\">EXERCICE 43</a></span></li></ul></div>"
]
},
{
......@@ -32,19 +32,41 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"### EXERCICE 52\n",
"### EXERCICE 43\n",
"\n",
"Reorganize the project in several files:\n",
"\n",
"- Only `loop()` and `main()` functions should remain in exercice52.cpp\n",
"- Exceptions should be separated in two files `Exception.cpp` and `Exception.hpp`.\n",
"- `PowerOfTwoApprox` will be put in two files: `PowerOfTwoApprox.hpp` and `PowerOfTwoApprox.hxx`. The latter will be included at the end of the former. You should also place in the same files the free functions that work closely with this class (e.g. `operator<<`).\n",
"- Only `loop()` and `main()` functions should be put in _main.cpp_.\n",
"- `Error` should be declared in `Error.hpp` and defined in `Error.cpp`.\n",
"- Free functions such as `times_power_of_2` and `round_as_int` should be put in `Tools` files. As all these definitions are template, they should be in a header file (that might be the same as the one used for declaration, or a different one if you want to split declaration and definition - in this case don't forget to include it at the end of the hpp file!)\n",
"- Likewise for `PowerOfTwoApprox`. Closely related free functions (such as `operator<<`) should be put in the same file(s).\n",
"- You may put all `TestDIsplay` functionalities together. Beware: some definitions are template and must be in header files, whereas some aren't and should therefore be compiler.\n",
"\n",
"**WARNING:** If you get complete template specializations, definition should be in a `cpp` file or inlined in header file!\n",
"- Do the same for the `TestDisplay` and its derived classes; you will need three files here (`cpp`, `hpp` and `hxx`).\n",
"- Finally, create two files `Tools.hpp` and `Tools.hxx` to put inside `times_power_of_2()`, `round_as_int` and `max_int`.\n",
"To do the exercice, create an _Exercice43_ directory in the TP root (and include it in your main CMakeLists.txt).\n",
"\n",
"The provided CMake contains already the steps to make this work."
"Create the files in this directory, and use the following CMakeLists.txt:\n",
"\n",
"````\n",
"add_library(exercice43_lib\n",
" SHARED\n",
" # Header files are not strictly necessary but may be useful for some CMake generators.\n",
" ${CMAKE_CURRENT_LIST_DIR}/Error.cpp\n",
" ${CMAKE_CURRENT_LIST_DIR}/Error.hpp\n",
" ${CMAKE_CURRENT_LIST_DIR}/PowerOfTwoApprox.hpp\n",
" ${CMAKE_CURRENT_LIST_DIR}/PowerOfTwoApprox.hxx\n",
" ${CMAKE_CURRENT_LIST_DIR}/TestDisplay.cpp\n",
" ${CMAKE_CURRENT_LIST_DIR}/TestDisplay.hpp\n",
" ${CMAKE_CURRENT_LIST_DIR}/TestDisplay.hxx\n",
" ${CMAKE_CURRENT_LIST_DIR}/Tools.hpp\n",
" ${CMAKE_CURRENT_LIST_DIR}/Tools.hxx)\n",
"\n",
"\n",
"add_executable(exercice43\n",
" ${CMAKE_CURRENT_LIST_DIR}/main.cpp)\n",
" \n",
"target_link_libraries(exercice43 \n",
" exercice43_lib)\n",
"```` "
]
},
{
......
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)
include(../../cmake/Settings.cmake)
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.")
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")
project(GettingStartedWithModernCpp_TP_RealEnv)
include(../../cmake/AfterProjectSettings.cmake)
add_executable(initial initial_file.cpp)
# add_library(exercice52_lib STATIC Exception.cpp TestDisplay.cpp)
# add_executable(exercice52 exercice52.cpp)
# target_link_libraries(exercice52 exercice52_lib)
\ No newline at end of file
include(${CMAKE_CURRENT_LIST_DIR}/Exercice43/CMakeLists.txt)
add_library(exercice43_lib
SHARED
# Header files are not strictly necessary but may be useful for some CMake generators.
${CMAKE_CURRENT_LIST_DIR}/Error.cpp
${CMAKE_CURRENT_LIST_DIR}/Error.hpp
${CMAKE_CURRENT_LIST_DIR}/PowerOfTwoApprox.hpp
${CMAKE_CURRENT_LIST_DIR}/PowerOfTwoApprox.hxx
${CMAKE_CURRENT_LIST_DIR}/TestDisplay.cpp
${CMAKE_CURRENT_LIST_DIR}/TestDisplay.hpp
${CMAKE_CURRENT_LIST_DIR}/TestDisplay.hxx
${CMAKE_CURRENT_LIST_DIR}/Tools.hpp
${CMAKE_CURRENT_LIST_DIR}/Tools.hxx)
add_executable(exercice43
${CMAKE_CURRENT_LIST_DIR}/main.cpp)
target_link_libraries(exercice43
exercice43_lib)
\ No newline at end of file
#include "Exception.hpp"
#include "Error.hpp"
Error::Error(std::string&& message)
Error::Error(const std::string& message)
: message_(message)
{ }
......@@ -9,12 +9,3 @@ const char* Error::what() const noexcept
{
return message_.c_str();
}
Overflow::Overflow()
: Error("An overflow occurred!")
{ }
Overflow::~Overflow() = default;
#ifndef EXCEPTION_HPP
# define EXCEPTION_HPP
#ifndef EXCEPTIONS_HPP
#define EXCEPTIONS_HPP
#include <exception>
#include <string>
#include <exception>
class Error : public std::exception
{
public:
//! Constructor.
Error(std::string&& message);
Error(const std::string& message);
//! Overrides what method.
virtual const char* what() const noexcept override;
......@@ -21,18 +20,4 @@ private:
};
class Overflow : public Error
{
public:
//! Constructor.
Overflow();
//! Destructor.
virtual ~Overflow();
};
#endif
#ifndef POWER_OF_TWO_APPROX_HPP
#define POWER_OF_TWO_APPROX_HPP
#include <iostream>
#include <string>
#include "Error.hpp"
#include "Tools.hpp"
//! 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;
//! Sign as a character: either '-' or ''.
// String might seem overkill, but empty char for positive case triggers a warnings.
std::string GetSignAsCharacter() const;
//! Sign as a number (e.g. 1 or -1).
IntT GetSignAsNumber() const;
private:
IntT numerator_ {};
int exponent_ {};
char sign_ { 1 };
};
template<class IntT>
std::ostream& operator<<(std::ostream& out, const PowerOfTwoApprox<IntT>& approximation);
//! 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);
/*!
* \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);
template<class IntT>
IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient);
#include "PowerOfTwoApprox.hxx"
#endif
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 {};
if (value < 0.)
{
sign_ = -1;
value = -value;
}
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_) * sign_ / denominator;
}
template<class IntT>
IntT PowerOfTwoApprox<IntT>::GetNumerator() const
{
return numerator_;
}
template<class IntT>
int PowerOfTwoApprox<IntT>::GetExponent() const
{
return exponent_;
}
template<class IntT>
std::string PowerOfTwoApprox<IntT>::GetSignAsCharacter() const
{
assert(sign_ == 1 || sign_ == -1);
if (sign_ == 1)
return "";
else
return "-";
}
template<class IntT>
IntT PowerOfTwoApprox<IntT>::GetSignAsNumber() const
{
return static_cast<IntT>(sign_);
}
template<class IntT>
std::ostream& operator<<(std::ostream& out, const PowerOfTwoApprox<IntT>& approximation)
{
out << approximation.GetSignAsCharacter() << PrintInt(approximation.GetNumerator()) << "/2^" << approximation.GetExponent();
return out;
}
template<class IntT>
IntT operator*(IntT coefficient, const PowerOfTwoApprox<IntT>& approx)
{
IntT product;
if (__builtin_mul_overflow(approx.GetNumerator(), coefficient, &product))
throw Error("Overflow! (in operator*(IntT, const PowerOfTwoApprox<IntT>&))");
return approx.GetSignAsNumber() * times_power_of_2(product, -approx.GetExponent());
}
template<class IntT>
IntT operator*(const PowerOfTwoApprox<IntT>& approx, IntT coefficient)
{
return coefficient * approx;
}
#include <iostream>
#include "TestDisplay.hpp"
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.
}
void TestDisplay::PrintOverflow(int Nbits, const Error& e) const
{
std::cout << "[With " << Nbits << " bits]: " << e.what() << std::endl;
}
#ifndef TEST_DISPLAY_HPP
# define TEST_DISPLAY_HPP
#define TEST_DISPLAY_HPP
#include <string>
#include <sstream>
#include "PowerOfTwoApprox.hpp"
#include "PowerOfTwoApprox.hpp"
#include "Tools.hpp"
/*!
* \brief Base class for TestDisplayPowerOfTwoApprox and TestDisplaySum.
*/
class TestDisplay
{
public:
//! Constructor.
//! Constructor which sets the only the resolution (i.e. the maximum index upon which error is defined).
TestDisplay(int resolution);
//! Virtual method.
virtual void operator()(int Nbits) const = 0;
//! Virtual destructor.
//! 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] after_bit_string String that might appear
* \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.
*/
void PrintNumericalError(int Nbits, double exact, double approx,
std::string&& after_bit_string, std::string&& before_error) const;
/*!
* \brief Print the line when an overflow occurred.
*/
void PrintOverflow(int Nbits) const;
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;
//! Function to call when an overflow occurred.
void PrintOverflow(int Nbits, const Error& e) const;
private:
//! Maximum error index.
//! Resolution.
const int resolution_;
};
/*!
* \brief Class in charge of displaying the approximation for PowerOfTwoApprox for a value which is expected
* to be provided in a derived class.
*/
template<class IntT>
class TestDisplayPowerOfTwoApprox : public TestDisplay
{
......@@ -59,77 +57,79 @@ public:
//! Constructor.
TestDisplayPowerOfTwoApprox(int resolution);
//! Virtual destructor.
virtual ~TestDisplayPowerOfTwoApprox();
//! Destructor
virtual ~TestDisplayPowerOfTwoApprox() override;
//! Pure virtual method Do().
virtual void operator()(int Nbits)const override = 0;
protected:
//! Print the output for `value` when `Nbits` are available.
//! Method in charge of the actual display.
void Display(int Nbits, double value) const;
};
//! Run TestDisplayPowerOfTwoApprox for value 0.65
template<class IntT>
class TestDisplayPowerOfTwoApprox065 : public TestDisplayPowerOfTwoApprox<IntT>
class TestDisplayPowerOfTwoApproxMinus065 : public TestDisplayPowerOfTwoApprox<IntT>
{
public:
//! Convenient alias.
using parent = TestDisplayPowerOfTwoApprox<IntT>;
//! Constructor.
TestDisplayPowerOfTwoApprox065(int resolution);
TestDisplayPowerOfTwoApproxMinus065(int resolution);
//! Destructor,
~TestDisplayPowerOfTwoApproxMinus065() override;
//! Print the output for the values 0.65 and 0.35.
void operator()(int Nbits) const override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
};
//! Run TestDisplayPowerOfTwoApprox for value 0.35
template<class IntT>
class TestDisplayPowerOfTwoApprox035 : public TestDisplayPowerOfTwoApprox<IntT>
{
public:
//! Convenient alias.
using parent = TestDisplayPowerOfTwoApprox<IntT>;
//! Constructor.
TestDisplayPowerOfTwoApprox035(int resolution);
//! Print the output for the values 0.65 and 0.35.
void operator()(int Nbits) const override;
//! Destructor,
~TestDisplayPowerOfTwoApprox035() override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
};
/*!
* \brief Class in charge of displaying the approximation for 0.65 * 3515 + 0.35 * 4832.
*/
template<class IntT>
class TestDisplaySum : public TestDisplay
class TestDisplayMultiply : public TestDisplay
{
public:
//! Constructor.
TestDisplaySum(int resolution);
explicit TestDisplayMultiply(int resolution);
//! To make the class a concrete one.
virtual ~TestDisplayMultiply() override;
//! Print the output for the values 0.65 and 0.35.
void operator()(int Nbits) const override;
//! Display the output for the chosen `Nbits`.
void operator()(int Nbits)const override;
private:
//! Print the output for the approximation of the sum value1 * coefficient1 + value2 * coefficient2.
//! Method in charge of the actual display.
void Display(int Nbits, double value1, IntT coefficient1, double value2, IntT coefficient2) const;