Commit b98ae015 authored by CHAMONT David's avatar CHAMONT David
Browse files

Review 4/1/categories Exercice

parent 4d4319b3
#include <iostream>
#include <chrono>
#include <type_traits>
#include <string>
// categories
struct TestTag {} ;
struct TestChronoTag {} ;
// tests
struct StringCopy
{
using category = TestChronoTag ;
char const * const title
{"Timing a std::string copy"} ;
void operator()() const
{
std::string s1 {"dummy"}, s2 ;
for ( int i = 0 ; i<1000 ; ++i )
{
if (i%2) s2 = s1 ;
else s1 = s2 ;
}
}
} ;
struct StringMove
{
using category = TestChronoTag ;
char const * const title
{"Timing a std::string move"} ;
void operator()() const
{
std::string s1 {"dummy"}, s2 ;
for ( int i = 0 ; i<1000 ; ++i )
{
if (i%2) s2 = std::move(s1) ;
else s1 = std::move(s2) ;
}
}
} ;
struct Accumulate
{
using category = TestTag ;
char const * const title
{"Test accumulating 0.1"} ;
void operator()() const
{
std::cout.precision(18) ;
double acc {} ;
for ( int i = 0 ; i<10 ; ++i )
{
acc += 0.1 ;
std::cout<<acc<<std::endl ;
}
}
} ;
// execution generic framework
template< typename TestT >
void execute_single_test()
{
TestT test ;
std::cout<<std::endl ;
std::cout<<"== " ;
std::cout<<(test.title)<<std::endl ;
if constexpr (std::is_same_v<typename TestT::category,TestChronoTag>)
{
using namespace std::chrono ;
auto t1 = steady_clock::now() ;
test() ;
auto t2 = steady_clock::now() ;
std::cout<<"Execution time: "
<<duration_cast<microseconds>(t2-t1).count()
<<" us."<<std::endl ;
}
else if constexpr (std::is_same_v<typename TestT::category,TestTag>)
{ test() ; }
}
template< typename... TestTs >
void execute()
{ ( execute_single_test<TestTs>() , ... ) ; }
// main program
int main()
{
execute<StringCopy,StringMove,Accumulate>() ;
}
#include <iostream>
#include <chrono>
#include <type_traits>
#include <string>
// external unmodifiable tests
struct Accumulate
{
char const * const title
{"Test accumulating 0.1"} ;
void operator()() const
{
std::cout.precision(18) ;
double acc {} ;
for ( int i = 0 ; i<10 ; ++i )
{
acc += 0.1 ;
std::cout<<acc<<std::endl ;
}
}
} ;
// your categories
struct TestTag {} ;
struct TestChronoTag {} ;
// your additional tests
struct StringCopy
{
using category = TestChronoTag ;
char const * const title
{"Timing a std::string copy"} ;
void operator()() const
{
std::string s1 {"dummy"}, s2 ;
for ( int i = 0 ; i<1000 ; ++i )
{
if (i%2) s2 = s1 ;
else s1 = s2 ;
}
}
} ;
struct StringMove
{
using category = TestChronoTag ;
char const * const title
{"Timing a std::string move"} ;
void operator()() const
{
std::string s1 {"dummy"}, s2 ;
for ( int i = 0 ; i<1000 ; ++i )
{
if (i%2) s2 = std::move(s1) ;
else s1 = std::move(s2) ;
}
}
} ;
// traits
template< typename TestT >
struct TestTraits
{ using category = typename TestT::category ; } ;
template<>
struct TestTraits<Accumulate>
{ using category = TestTag ; } ;
// execution generic framework
template< typename TestT >
void execute_single_test()
{
TestT test ;
std::cout<<std::endl ;
std::cout<<"== " ;
std::cout<<(test.title)<<std::endl ;
if constexpr (std::is_same_v<typename TestTraits<TestT>::category,TestChronoTag>)
{
using namespace std::chrono ;
auto t1 = steady_clock::now() ;
test() ;
auto t2 = steady_clock::now() ;
std::cout<<"Execution time: "
<<duration_cast<microseconds>(t2-t1).count()
<<" us."<<std::endl ;
}
else if constexpr (std::is_same_v<typename TestTraits<TestT>::category,TestTag>)
{ test() ; }
}
template< typename... TestTs >
void execute()
{ ( execute_single_test<TestTs>() , ... ) ; }
// main program
int main()
{
execute<StringCopy,StringMove,Accumulate>() ;
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Un apercu de C++20\n",
"\n",
"Le standard à venir fourmille de multiples micro-nouveautés, mais nous allons\n",
"nous focaliser sur les quatre nouveautés très importantes que Rainer Grimm\n",
"appelle \"The Big Four\", et qui vont nous faire basculer dans le C++\n",
"\"post-moderne\" :\n",
"\n",
"1. en matière de compilation : les [**modules**](fr.1-modules.ipynb) ;\n",
"1. côté programmation générique : les [**concepts**](fr.2-concepts.ipynb) ;\n",
"1. côté programmation fonctionnelle : les [**ranges**](fr.3-ranges.ipynb) ;\n",
"1. côté programmation asynchrone : les [**co-routines**](fr.4-coroutines.ipynb).\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"AVERTISSEMENT : toutes ces fonctionnalités sont plutôt nouvelles, ne vous attendez pas à des explications très avancées."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"---\n",
"## Sources\n",
"\n",
"* [Modernes Cpp](http://modernescpp.com/index.php/thebigfour)\n",
"* [Cpp Reference](https://en.cppreference.com/w/cpp/compiler_support)\n",
"* [Wikipedia](https://en.wikipedia.org/wiki/C%2B%2B20)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"© *CNRS 2020* \n",
"*Assembled and written by David Chamont, this work is made available according to the terms of the* \n",
"[*Creative Commons License - Attribution - NonCommercial - ShareAlike 4.0 International*](http://creativecommons.org/licenses/by-nc-sa/4.0/)"
]
}
],
"metadata": {
"celltoolbar": "Diaporama",
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Modules C++20 : une compilation améliorée ?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Motivation\n",
"\n",
"Malgré toutes ses nouveautés, le C++ moderne utilise encore la mécanique préhistorique d'inclusion par `#include`, qui cumule beaucoup de défauts."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"#### Explosion du temps de compilation"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Pour chaque unité de compilation (fichier de corps à compiler séparemment), le compilation doit inclure tous les fichiers d'entête inclus, et toutes leurs inclusions récursivement, analyser et compiler l'ensemble. Cela énormément de travail redondant, encore plus avec le code récent riche en templates. Des techniques de pré-compilation des fichiers d'entête ont tenté de répondre à ce problème, sans réussir à s'imposer."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"#### Fragilité"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"L'interprétation d'un fichier inclus est très dépendante des éventuelles macros définies auparavant, ce qui peut générer des bogues inextriquables."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"#### Pollution du code"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Pour palier à certains problèmes liés au pré-processeur, un certains nombres de pratiques sont devenues obligatoires, telles que les *include guards*, qui complique l'apprentissage des nouveaux venus, saoulent les experts, et obscurcissent inutilement le code."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"#### Confusion des outils"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Le système de pré-processing complique singulièrement la tâche des outils de développement, en particulier lorsqu'ils essaient d'établir ce qui constitue l'interface d'une bibliothèque..."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Idée générale\n",
"\n",
"Déjà envisagés pour C++11, plusieurs fois repoussés, les **modules C++** veulent supprimer le recours au préprocesseur, mettre fin à la distinction habituelle entre fichiers d'entêtes et fichiers de corps, mieux exprimer la structure logique du code, et bien sur **accélérer la compilation**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Utiliser un module"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"On veut pouvoir **importer des modules** aussi naturellement qu'on le fait déjà dans d'autres langages plus récents. Si la bibliothèque standard était écrite comme un seul module (ce qui n'est pas encore le cas), on pourrait écrire :"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"```c++\n",
"import std ;\n",
"int main() {\n",
" std::cout << “Hello World\\n”;\n",
"}\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Définir un module... la terminologie"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Pour un **module logique** défini par le développeur, ce dernier peut simplement mettre tout ce qui est nécessaire dans un seul fichier, dit **module complet**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Cependant, afin de bénéficier de l'efficacité des systèmes de construction externes, il est plus probable qu'il divise ce module logique en plusieurs fichiers physiques, appelés **unités de module**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Une configuration typique, pour chaque module individuel, serait d'avoir un fichier de type *en-tête*, appelé **unité primaire d'interface**, et un fichier de type *corps*, appelé **unité d'implémentation**. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Si l'on veut diviser le module encore plus, on peut subdiviser les unités en **partitions d'interface de module** et **partitions d'implémentation de module**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Finalement, chaque **unité** est constituée d'un ou plusieurs **fragments** de différents types : **global**, **purview**, **private**, **partitions**... Seuls certains mélanges prédéfinis de fragments sont autorisés, correspondant à différents types d'unités."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"**A noter** : certaines unités de module sont des sortes de fichiers d'en-tête/interface, mais elles doivent maintenant être compilées !"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Diversité des stratégies et des mises en œuvre\n",
"\n",
"> there is no one way of correctly using C++20 modules\". *[Niall Cooling](https://blog.feabhas.com/2021/08/c20-modules-with-gcc11/)*"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"The C++20 standard does not enforce a single way to split the modules into files, the file suffix, the use of auxiliary directories/files... the implementation of **C++ modules may differ significantly between compilers**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"In the following examples, we will investigate the GCC implementation, and we will stick to the usual *interface+implementation* pair of files for each module, similar to the old-school *header+body* files. This is somehow a modern implementation of the old \"precompiled header files\"."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Mon premier module GCC"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"GCC n'introduit pas de nouvelles extensions de fichiers, c'est à vous de définir une convention. Utilisons un *pre-suffixe* **`_mh`** pour un fichier de type *header*, et **`_mb`** pour un fichier de type *body*."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"GCC requiert les options **`-std=c++20 -fmodules-ts`**, et produira un fichier objet pour chaque fichier module."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Avertissement : GCC **exige** que l'interface du module soit compilée avant l'implémentation du module."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "subslide"