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

Revamp last TP.

parent 0400e7fe
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Useful concepts and STL](/notebooks/5-UsefulConceptsAndSTL/0-main.ipynb)
%% Cell type:markdown id: tags:
* [Error handling](/notebooks/5-UsefulConceptsAndSTL/1-ErrorHandling.ipynb)
* [TP 14](/notebooks/5-UsefulConceptsAndSTL/1b-TP.ipynb)
* [RAII idiom](/notebooks/5-UsefulConceptsAndSTL/2-RAII.ipynb)
* [Containers](/notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb)
* [TP 15](/notebooks/5-UsefulConceptsAndSTL/3b-TP.ipynb)
* [Associative containers](/notebooks/5-UsefulConceptsAndSTL/4-AssociativeContainers.ipynb)
* [Move semantics](/notebooks/5-UsefulConceptsAndSTL/5-MoveSemantics.ipynb)
* [TP 16](/notebooks/5-UsefulConceptsAndSTL/5b-TP.ipynb)
* [Smart pointers](/notebooks/5-UsefulConceptsAndSTL/6-SmartPointers.ipynb)
* [TP 17](/notebooks/5-UsefulConceptsAndSTL/6b-TP.ipynb)
* [TP 16](/notebooks/5-UsefulConceptsAndSTL/6b-TP.ipynb)
* [Algorithms](/notebooks/5-UsefulConceptsAndSTL/7-Algorithms.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)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [C++ in a real environment](/notebooks/6-InRealEnvironment/0-main.ipynb) - [TP 17](/notebooks/6-InRealEnvironment/2b-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-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>
%% Cell type:markdown id: tags:
### Introduction
[This notebook](/notebooks/TP/HowTo.ipynb) explains very briefly your options to run the TP.
**WARNING:** Coliru is not an option here: you need to go the local or Docker way!
%% Cell type:markdown id: tags:
### EXERCICE 52
### EXERCICE 43
Reorganize the project in several files:
- Only `loop()` and `main()` functions should remain in exercice52.cpp
- Exceptions should be separated in two files `Exception.cpp` and `Exception.hpp`.
- `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<<`).
**WARNING:** If you get complete template specializations, definition should be in a `cpp` file or inlined in header file!
- Do the same for the `TestDisplay` and its derived classes; you will need three files here (`cpp`, `hpp` and `hxx`).
- Finally, create two files `Tools.hpp` and `Tools.hxx` to put inside `times_power_of_2()`, `round_as_int` and `max_int`.
The provided CMake contains already the steps to make this work.
- Only `loop()` and `main()` functions should be put in _main.cpp_.
- `Error` should be declared in `Error.hpp` and defined in `Error.cpp`.
- 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!)
- Likewise for `PowerOfTwoApprox`. Closely related free functions (such as `operator<<`) should be put in the same file(s).
- 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.
To do the exercice, create an _Exercice43_ directory in the TP root (and include it in your main CMakeLists.txt).
Create the files in this directory, and use the following 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)
````
%% 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)
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.