UniversitØ Grenoble alpes.
Auteur
Version : 22 septembre 2017
Didacticiel
Bases de programmation en C++
Table des mati?res
1 Introduction 11
1.1 Choix du syst?me d’exploitation . . . . . . . . . . . . . . . . . . . . . . . . 12
1.1.1 Installer Xubuntu et l’environnement de programmation c++ sur
son ordinateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2 Environnement de programmation . . . . . . . . . . . . . . . . . . . . . . . 13
1.2.1 Utilisation de commandes dans un terminal . . . . . . . . . . . . . 13
1.2.1.1 Les rØpertoires . . . . . . . . . . . . . . . . . . . . . . . . 14
1.2.1.2 Exercices sur les rØpertoires . . . . . . . . . . . . . . . . . 14
1.2.1.3 Les chiers (en exercice) . . . . . . . . . . . . . . . . . . . 15
1.2.1.4 La documentation sur les commandes unix . . . . . . . . . 15
1.2.2 Programmer avec l’Øditeur emacs et une fenŒtre de commandes . . . 15
1.2.2.1 ParamØtrage de emacs . . . . . . . . . . . . . . . . . . . . 17
I Bases du langage C++ 18
2 Le tout dØbut. Lire le clavier et a cher l’Øcran 19
2.1 A chage dans un terminal . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Lire le clavier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3 DØclaration et a ectation des objets 22
3.1 Objets de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.1.1 Les dØclarations d’objets de base . . . . . . . . . . . . . . . . . . . 22
3.1.2 Initialisation d’un objet de base . . . . . . . . . . . . . . . . . . . . 22
3.1.2.1 Remarques . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.1.3 A chage des nombres avec une prØcision donnØe . . . . . . . . . . . 24
3.1.4 Attention la division Euclidienne . . . . . . . . . . . . . . . . . . 24
3.1.5 Pour gØnØrer un nombre entier p alØatoire . . . . . . . . . . . . . . 25
3.2 Objets plus ØlaborØs de la classe standard . . . . . . . . . . . . . . . . . . 26
3.2.1 Chaines de caract?res (string) . . . . . . . . . . . . . . . . . . . . . 26
3.2.1.1 Conversion de chi res en chaine de caract?re et inverse-
ment. Solution simple. . . . . . . . . . . . . . . . . . . . . 26
3.2.2 Nombres complexes . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.2.3 Liste d’objets (ou tableau de dimension 1) : vector . . . . . . . . . . 28
3.3 Vecteurs et matrices pour l’alg?bre linØaire avec la librairie Armadillo . . . 30
3.3.1 Quelques fonctions utiles sur les vecteurs et matrices . . . . . . . . 31
3.4 Objets graphiques avec la librairie Root . . . . . . . . . . . . . . . . . . . . 32
3.4.1 Commande de compilation . . . . . . . . . . . . . . . . . . . . . . . 33
3.4.2 Exemple de dØpart qui dessine un cercle . . . . . . . . . . . . . . . 33
3.4.3 Quelques objets graphiques . . . . . . . . . . . . . . . . . . . . . . 36
3.5 SupplØments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.5.1 Continuer une ligne de c++ la ligne suivante avec \ . . . . . . . . 37
3.5.2 Sur les types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.5.2.1 Renommage . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.5.2.2 Connaitre le type . . . . . . . . . . . . . . . . . . . . . . . 38
3.5.3 Quelques opØrations ØlØmentaires . . . . . . . . . . . . . . . . . . . 39
3.5.4 Sur les chaines de caract?res . . . . . . . . . . . . . . . . . . . . . . 39
3.5.4.1 Manipulations. Recherche dans une chaine, extraction . 39
ment. Solution sophistiquØe (permet des mØlanges de type) 40
3.5.4.3 Conversion nombre <-> chaine de caractere qui reprØsente
le nombre en base 2 . . . . . . . . . . . . . . . . . . . . . 41
3.5.4.4 Conversion de string vers char* . . . . . . . . . . . . . . . 42
3.5.5 Quelques opØrations en base 2 (binaire, les bits) et en base 16 (hexa-
decimale) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.5.6 tuple : ensemble d’objets divers . . . . . . . . . . . . . . . . . . . . 43
3.5.7 Vecteurs et matrices avec la librairie armadillo . . . . . . . . . . . . 44
3.5.7.1 Diagonalisation d’une matrice symØtrique rØelle . . . . . . 44
3.5.7.2 Diagonalisation d’une matrice hermitienne complexe . . . 45
4 Les instructions de base 48
4.1 La boucle "for" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.2 Ecrire une condition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.2.1 Les opØrateurs de comparaison : . . . . . . . . . . . . . . . . . . . 50
4.2.2 Les opØrateurs logiques . . . . . . . . . . . . . . . . . . . . . . . . 51
4.2.3 Remarque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.3 La boucle do {..} while (..); . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.4 La boucle while (..) {..}; . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.5 L’instruction if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.6 break : pour sortir de la boucle. continue : pour sauter l’instruction. . . . . 54
5 Les fonctions 55
5.1 Exemple de fonction qui renvoit un objet . . . . . . . . . . . . . . . . . . . 55
5.2 Exemple de fonction qui ne renvoit rien . . . . . . . . . . . . . . . . . . . . 56
5.3 Param?tres des fonctions par rØfØrence . . . . . . . . . . . . . . . . . . . . 57
5.4 La surcharge des fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . 58
5.4.1 Exemple : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6 Les pointeurs 60
6.1 DØclaration et a ectation d’un pointeur . . . . . . . . . . . . . . . . . . . . 60
6.1.1 Param?tres de la fonction main() . . . . . . . . . . . . . . . . . . . 61
6.2.0.1 Remarques . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.3 Utilisation des pointeurs avec la librairie graphique ROOT . . . . . . . . . 65
6.3.1 Utiliser une liste d’ellipses . . . . . . . . . . . . . . . . . . . . . . . 65
6.3.1.1 Remarques : . . . . . . . . . . . . . . . . . . . . . . . . . 66
6.3.2 Utiliser un pointeur sur l’ellipse . . . . . . . . . . . . . . . . . . . . 66
6.3.2.1 Remarques : . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7 CrØation d’une classe 69
7.1 Exemple (Øtape1) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.1.1 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.2 Exemple (Øtape2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.2.1 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
7.3 Utilisation de pointeurs sur objets . . . . . . . . . . . . . . . . . . . . . . . 73
8 Les chiers 74
8.1 Ecriture de donnØes dans un chier . . . . . . . . . . . . . . . . . . . . . . 74
8.1.0.1 Remarque : . . . . . . . . . . . . . . . . . . . . . . . . . . 74
8.1.0.2 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
8.2 Lecture de donnØes depuis un chier . . . . . . . . . . . . . . . . . . . . . 75
8.2.0.1 Remarques : . . . . . . . . . . . . . . . . . . . . . . . . . 76
9 Micro-projets 1 78
9.1 Que faire avant de programmer? . . . . . . . . . . . . . . . . . . . . . . . 78
9.1.1 Quelques notions de qualitØ en informatique . . . . . . . . . . . . . 79
9.2 Suite de Syracuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
9.3 Pendule simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
9.4 La fractale du dragon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
9.5 Images subliminales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.6 Equation de la chaleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.7 Le jeu puissance 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
9.8 Le jeu de la vie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
9.9 Algorithme de Monte-Carlo pour le mod?le d’Ising . . . . . . . . . . . . . . 85
9.9.1 Algorithme de Monte-Carlo . . . . . . . . . . . . . . . . . . . . . . 86
9.10 Zeros d’un polynome alØatoire . . . . . . . . . . . . . . . . . . . . . . . . . 87
II ComplØments sur le langage C++ 89
10 Autres environnements 90
10.1 Programmer avec l’environnement codeblocks . . . . . . . . . . . . . . . . 90
11 Ecrire et lire des donnØes en binaires sur un chier (ou un ux) 92
11.1 Bits, octets, bytes, taille d’un objet . . . . . . . . . . . . . . . . . . . . . . 92
11.3 Fichiers en format binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
11.3.0.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
11.3.0.2 Remarques . . . . . . . . . . . . . . . . . . . . . . . . . . 95
12 Quelques commandes utiles 96
12.0.1 La commande system . . . . . . . . . . . . . . . . . . . . . . . . . . 96
13 Classes (suite) 97
13.1 SurdØ nitions d’opØrateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
13.1.1 Exemple d’opØrateur binaire . . . . . . . . . . . . . . . . . . . . . . 97
13.1.2 Commentaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
13.1.3 Exemple d’un opØrateur unaire . . . . . . . . . . . . . . . . . . . . 98
13.1.4 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
13.2 Fonctions amies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
13.2.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
13.2.2 Commentaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
13.2.3 Exemple de cout << . . . . . . . . . . . . . . . . . . . . . . . . . . 100
13.3 Vecteurs de taille variable . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
13.3.2 OpØrateur d’a ectation = . . . . . . . . . . . . . . . . . . . . . . . 102
14 Gestion de projets avec chiers .h, Make le, Git et Doxygen 104
14.1 Projet avec plusieurs chiers, chier .h . . . . . . . . . . . . . . . . . . . . 104
14.1.0.1 RØsultat du programme : . . . . . . . . . . . . . . . . . . 104
14.1.0.2 Code c++ . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
14.1.0.3 Commandes de compilation . . . . . . . . . . . . . . . . . 106
14.2 Un programme Make le pour gØrer la compilation . . . . . . . . . . . . . . 107
14.2.1 Un programme Make le plus pratique . . . . . . . . . . . . . . . . . 108
14.2.2 Generation automatique du chier make le? . . . . . . . . . . . . . 109
14.3 Gestion de versions d’un projet avec Git . . . . . . . . . . . . . . . . . . . 109
14.3.0.1 Utilisation de Git sur son ordinateur . . . . . . . . . . . . 109
14.4 Collaboration et partage de projets sur internet avec GitHub . . . . . . . . 109
14.5 Commenter et documenter un projet avec Doxygen . . . . . . . . . . . . . 110
14.5.1 CrØation et consultation de la documentation . . . . . . . . . . . . 110
14.5.1.1 CrØation de la documentation html . . . . . . . . . . . . . 110
14.5.1.2 consultation de la documentation . . . . . . . . . . . . . . 110
14.5.2 conventions pour Øcrire les commentaires . . . . . . . . . . . . . . . 110
14.5.2.1 Page principale : . . . . . . . . . . . . . . . . . . . . . . . 110
14.5.3 Fichier de commentaires avec la syntaxe markdown . . . . . . . . . 114
14.6 Compte rendu de projet en pdf et html avec Lyx . . . . . . . . . . . . . . . 115
15 Interface interactive pour le C++ 116
III ComplØments sur les librairies du langage C++ 117
16 Librairie Root : graphisme et autre.. (complØments) 119
16.1 Graphisme de base 2Dim . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
16.1.1 Objets graphiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
16.1.2 Dessiner dans plusieurs fenŒtres . . . . . . . . . . . . . . . . . . . . 121
16.1.3 Un cadre avec des axes graduØs : DrawFrame . . . . . . . . . . . . . 121
16.1.4 Un seul axe graduØ : TGaxis . . . . . . . . . . . . . . . . . . . . . . 123
16.1.5 Couleurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
16.2 Graphisme 1D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
16.2.1 Courbe a partir de l’expression d’une formule y = f (x) : TF1 . . . 125
16.2.1.1 Exemple partir d’une formule littØrale : . . . . . . . . . 125
16.2.1.2 MŒme rØsultat partir d’une fonction C++ . . . . . . . . 125
16.2.2 Histograme 1Dim ou Courbe y (x) partir de tableau de valeurs
y (i) : TH1D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
16.2.2.1 ReprØsentation en diagramme Camembert (Pie Chart) . 127
16.2.3 ReprØsentation d’un tableau de valeurs y (i) sous forme de camem-
16.2.4 Courbe paramØtrØe (x(i),y (i)) avec axes partir de tableaux de
valeurs x(i),y (i) : TGraph . . . . . . . . . . . . . . . . . . . . . . 128
16.2.5 Courbe paramØtrØe (r (i),? (i)) reprØsentØe en coordonnØes polaire
partir de points r (i),? (i) . . . . . . . . . . . . . . . . . . . . . . . 129
16.2.6 CrØe histogramme ou fonction 1D y = f (x) par tirage alØatoire et
ajustement par une formule : TF1 . . . . . . . . . . . . . . . . . . . 131
16.2.7 Superposition d’histogrammes : THStack . . . . . . . . . . . . . . . 132
16.3 Graphisme 2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
16.3.1 Surface 2Dim : z = f (x,y) partir d’un tableau de valeurs z (i,j) :
TH2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
16.3.2 Surface 2D a partir de l’expression d’une formule z = f (x,y) : TF2 134
16.3.3 Surface 2D z (x,y) partir d’un ensemble arbitraire de points x(i),y (i),z (i) :
TGraph2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
16.3.4 Champ de vecteur en dimension 2 . . . . . . . . . . . . . . . . . . . 136
16.4.1 Des points dans R3 : TPolyMarker3D . . . . . . . . . . . . . . . . . 137
16.4.2 Surface dØterminØe par f (x,y,z) = 0 : TF3 . . . . . . . . . . . . . 138
16.4.3 Fonction f (x,y,z) ? R : TH3 . . . . . . . . . . . . . . . . . . . . . 139
16.4.3.1 Dessin d’une surface de niveau souhaitØe . . . . . . . . . . 140
16.4.3.2 Dessin d’une courbe sur une surface . . . . . . . . . . . . 142
16.5 Utilisation de la souris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
16.5.1 Pour qu’un objet graphique capture les evenements souris . . . . . 145
16.5.2 Pour gØrer la souris lorsque le programme calcule . . . . . . . . . . 146
16.6 Comment crØer un chier gif animØ . . . . . . . . . . . . . . . . . . . . . . 146
16.7 Boutons de commandes (Widgets et GUI) . . . . . . . . . . . . . . . . . . 147
16.7.1 Exemple simple avec quelques widgets . . . . . . . . . . . . . . . . 147
16.7.2 Exemple simple avec un menu, des zones, des tabs et une fenetre
secondaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
16.7.3 Construire une interface de widgets la souris avec GuiBuilder . . . 162
16.8 Autres trucs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
16.8.0.1 Nombre alØatoire avec Root . . . . . . . . . . . . . . . . . 162
16.8.0.2 Quelques Options gØnØrales de Root . . . . . . . . . . . . 162
17 Mesure du temps 163
17.1 Mesure de la date et du temps ØcoulØ . . . . . . . . . . . . . . . . . . . . . 163
17.1.0.2 A chage de la date . . . . . . . . . . . . . . . . . . . . . 164
17.2 Attendre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
17.2.1 Attendre une certaine durØe . . . . . . . . . . . . . . . . . . . . . . 164
17.2.2 Attendre jusqu’ une certaine date . . . . . . . . . . . . . . . . . . 164
18 IntØgrer des Øquations di Ørentielles ordinaires (EDO) avec la librairie
ODEINT 166
19 Lecture et Øcriture d’un chier son (audio) de format WAV 171
19.0.0.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 171
19.0.0.2 Travail prØalable pour le projet C++ . . . . . . . . . . . . 171
19.0.1 Programme pour Øcrire un chier WAV : . . . . . . . . . . . . . . . 171
19.0.2 Programme pour lire un chier WAV : . . . . . . . . . . . . . . . . 174
20 Gestion de la carte son (audio temps rØel) avec la librairie sndio 176
20.1 Installation ( faire la premi?re fois) . . . . . . . . . . . . . . . . . . . . . . 176
20.1.0.1 Travail prØalable pour le projet C++ . . . . . . . . . . . . 177
20.2 Utilisation du micro (entrØe) et du haut-parleur (sortie) . . . . . . . . . . . 178
20.2.1 Le micro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
20.2.2 Le haut-parleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
21 Plusieurs programmes en parall?le qui communiquent, avec thread 183
21.1 Lancement de threads ou processus . . . . . . . . . . . . . . . . . . . . . . 184
21.1.0.1 Commande de compilation : . . . . . . . . . . . . . . . . . 184
21.1.0.2 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . 185
21.1.0.3 Lancer un thread d’une fonction membre de classe . . . . 185
21.1.0.4 Lancer une liste de thread (tableau) . . . . . . . . . . . . 186
21.1.0.5 Lancer une liste de thread (vector) . . . . . . . . . . . . . 186
21.2 Mutex pour annoncer occupØ/libre . . . . . . . . . . . . . . . . . . . . . . 187
21.2.0.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 187
21.2.0.2 Remarques . . . . . . . . . . . . . . . . . . . . . . . . . . 187
21.2.0.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
21.2.0.4 RØsultat : . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
21.3 Communications : variables conditionnelles pour rØveiller un programme en
attente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
21.3.0.1 Exemple de base oø un thread endormi est reveillØ par un
autre. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
22 Messages MIDI Musical temps rØel avec la librairie sndio 196
22.1 Installation ( faire la premi?re fois) . . . . . . . . . . . . . . . . . . . . . . 196
22.2 Exemple ØlØmentaire sur l’envoi et reception de messages MIDI . . . . . . . 196
22.3 Exemple d’envoie de message MIDI un synthØtiseur . . . . . . . . . . . . 200
22.4 Reception de notes midi depuis un piano Ølectrique . . . . . . . . . . . . . 204
23 Messages MIDI (Musique) sur chier 207
23.1 Structure d’un chier SMF . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
23.1.1 Premier bloc (header chunk) . . . . . . . . . . . . . . . . . . . . . . 207
23.1.2 Bloc de piste (Track chunk) . . . . . . . . . . . . . . . . . . . . . . 208
23.1.3 Message Midi standard M . . . . . . . . . . . . . . . . . . . . . . . 209
23.1.4 Message mØta Øv?nement M (pour les signaux midi sur chier et non
pas temps rØel) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
23.1.5 Message du syst?me (System Exclusive Message (SysEx)) M . . . . 209
23.1.6 Message temps rØel M (pour les signaux midi temps rØel et non sur
chier) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
23.1.7 Control Change pour les message x1 = xBc . . . . . . . . . . . . . 210
23.1.8 type pour les Meta-Evenement . . . . . . . . . . . . . . . . . . . . . 211
24 Autres librairies conseillØes (?) 213
24.1 alg?bre linØaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
24.2 Images, vidØos, graphisme et boites de dialogues . . . . . . . . . . . . . . . 213
25 Le traitement d’images et de vidØos avec la librairie OpenCV 214
25.1 Commande de compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
25.2 Lecture et ecriture de chiers images . . . . . . . . . . . . . . . . . . . . . 214
25.2.0.1 Fonctions utiles sur les images . . . . . . . . . . . . . . . 215
25.3 Utilisation de la souris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
25.4 Lecture et Øcriture d’un chier video ou de la webcam . . . . . . . . . . . . 219
25.5 DØtection d’objet par leur couleur . . . . . . . . . . . . . . . . . . . . . . . 220
25.6 DØtection et suivit d’objet de couleur avec l’algorithme CamShift . . . . . 222
26 Conversion d’un programme C++ en programme Java Script avec em-
scripten 230
26.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
26.2 Conversion C++ -> Javascript . . . . . . . . . . . . . . . . . . . . . . . . 231
26.2.1 Autres exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
26.3.1 Exemple de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
26.3.2 Exemple avec des Øchanges plus complexes . . . . . . . . . . . . . . 234
26.3.2.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
26.4 Exemples de forms en html . . . . . . . . . . . . . . . . . . . . . . . . . . 237
26.5 Appeler des fonctions js depuis c++ et des fonctions c++ depuis js . . . . 241
26.6 Autres remarques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
26.7 Graphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
26.8 Notes musicales en html5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
26.9 Utilisation des capteurs d’un smartphone . . . . . . . . . . . . . . . . . . . 243
26.10Utilisation de chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
27 CrØation automatique de Make le et d’interface graphique utilisateur
(GUI) 245
27.1 Mode d’emploi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
27.1.0.1 Fonctionnement nal des commandes . . . . . . . . . . . . 245
27.1.0.2 sortie du programme makef . . . . . . . . . . . . . . . . . 246
27.1.1 Instructions pour placer les widgets . . . . . . . . . . . . . . . . . . 246
28 A propos des licences 248
Suivre le didacticiel Partie I Bases du langage C++ I, en binome ou seul.
Lire l’introduction 1, puis passer la partie I, I.
Faire tous les exercices demandØs. Pour les exercices marquØs par (*). Par exemple tabulation (*), Øcrire un programme et le montrer l’enseignant (ou montrer plusieurs exercices la fois).
On peut seulement lire rapidement les parties appelØes supplØments .
Ce didacticiel se termine par la rØalisation d’un microprojet en Section 9. Choisir parmi la liste proposØe. RØdiger un compte rendu de 2 ou 3 pages avec lyx, avec des images et des formules. Exporter en pdf et en xhtml et le montrer l’enseignant, avec un oral de 5mn-10mn.
Optionellement, suivre le didacticiel Partie II ComplØments du langage
C++ II.
Choisir un projet d’expØrimentation numØrique. Voir la page projets. A la derni?re sØance, chaque Øtudiant (ou binome) prØsente son projet sur vidØo projecteur, pendant 15 20 mn, l’ensemble de la classe.
Cette prØsentation devra Œtre prØparØe avec (qui permet de crØer des documents scienti ques) et exportØe en html (avec LyxHtml) ou en pdf.
Pour apprendre Lyx, voici un document en pdf qui sert d’exemple et d’exercice. Voici le mŒme document en html, qui vous servira Øventuellement pour copier/coller.
Chapitre 1
Introduction
Ce didacticiel vous permettra d’apprØhender le langage de programmation C++ (version C++11) et ses avantages. Dans ce didacticiel, les exercices marquØs par (*) seront vØri Øs par l’enseignant pendant les sØances. Il y a de nombreux liens html qui renvoie vers plus d’informations.
Pourquoi enseigner le C++ l’universitØ, dans la li?re physique ? d’une part c’est un langage qui est tr?s bien adaptØ pour la simulation ou l’analyse de probl?mes physiques et de fa on plus gØnØrale pour le travail scienti que. Dans les laboratoires il a remplacØ peu peu le langage fortran. Le langage python est un autre langage qui utilise aussi des classes et est aussi tr?s utilisØ dans le domaine scienti que. Un programme en python est peut Œtre plus simple mettre en oeuvre qu’un programme en C++, c’est une histoire de goßt et d’habitude, mais il est aussi beaucoup plus lent l’exØcution. Un avantage du langage python est qu’il est interprØtØ : on peut exØcuter un programme ligne apr?s ligne alors que le langage C++ est compilØ : un programme appelØ compilateur le transforme en langage machine ( chier exØcutable) avant qu’il ne soit exØcutØ et cela le rend rapide. La derni?re version C++11 fait du C++ un langage aussi conviviale et simple d’utilisation que le langage python (et plus rapide!).
Nouvelles version du C++:les rØcentes versions puis C++14, C++17, C++20 ont pour vocation de rendre le C++ plus facile apprendre et toujours performant. RØfØrences :
Sur le web :
11
voici un site tr?s utile : Il contient un de tr?s bonne qualitØ, oø les exemples peuvent Œtre essayØ en ligne (avec la petite ic ne edit & rundroite de example, essayez par exemple sur setprecision)!. Il contient aussi une compl?te au langage C++.
Signalons le cours en ligne en francais sur le C++ de Coursera sur : ce sont des courtes, ou Cours de C++ de OpenClassRoom en fran ais. La page web de auteur du langage C++, et en particulier la FAQ sur le C++11
Un MOOC sur Linux. Un MOOC sur le C++. Un autre MOOC sur le C++.
Livre pour le langage C++ :
Programmer en C++ de Claude Delannoy, Øditions Eyrolles
The C++ programming langage de Bjarne Stroustroup, 4eme Ødition.
Livre pour le langage C : Le langage C de B.W. Kernighan et D.M. Ritchie edition Masson.
1.1 Choix du syst?me d’exploitation
Ce didacticiel est indØpendant du syst?me d’exploitation. Cependant en sØance de TP on travaille avec linux et plus particuli?rement avec la distribution ou Ubuntu. On conseille cette distribution gratuite qui peut Œtre installØe sur votre ordinateur personnel en suivant ce lien :
Il y a cependant possibilitØ d’utiliser d’autres syst?mes comme windows, mac-os, etc
Dans ce didacticiel, nous utiliserons les logiciels et librairies suivantes qui sont en gØnØral tØlØchargeables depuis votre distribution :
1.1.1 Installer Xubuntu et l’environnement de programmation c++
sur son ordinateur
Si votre ordinateur marche avec windows, vous pouvez installer rajouter ubuntu (linux) soit avec une machine virtuelle virtualbox ou installer Ubuntu en double boot (Suivre les instructions sur ) ou installer directement ubuntu sous windows et doc sur WSL.
Si ubuntu (linux) est dØj installØ sur votre ordinateur, il faut installer les librairies requises pour ce didacticiel. Pour cela, ouvrir un terminal et Øcrire, l’une apr?s l’autre les lignes suivantes :
sudo apt-get install -y root-system # librairie Root du Cern
sudo apt-get install -y emacs emacs-goodies-el global clang auto-complete-el
cscope-el # logiciel pour recherche de fonctions etc sudo apt-get install -y libarmadillo-dev # C++ linear algebra
Autre info:.
1.2 Environnement de programmation
Avec un petit exemple nous allons voir comment :
1. Øcrire le code d’un programme en C++
2. le compiler, c’est dire que l’ordinateur traduit le programme C++ en un programme exØcutable (en langage machine) qu’il pourra exØcuter ensuite.
3. exØcuter le programme
4. Øventuellement de le dØbugger (cela signi e recherche d’erreurs) : si le programme ne fonctionne pas comme on le dØsire, on l’exØcute instruction apr?s instruction a n de suivre ce qu’il fait pas pas, dans le but de trouver un bug (= une erreur ).
Dans la Section suivante nous e ectuons ces taches avec l’Øditeur et une fenŒtre de commandes (terminal).
1.2.1 Utilisation de commandes dans un terminal
Si vous avez une question sur l’utilisation de ubuntu, par exemple comment ouvrir et utiliser un terminal, Øcrire dans google : ubuntu terminal .
Dans ubuntu, ouvrir une fenŒtre de commandes (ou terminal). Dans cette fenŒtre de commandes, vous pouvez Øcrire des commandes l’ordinateur. Ces commandes sont dans le langage UNIX, syst?me avec lequel fonctionne l’ordinateur. Cette section vous explique les principales commandes de UNIX qu’il faut conna tre : les commandes qui permettent de gØrer l’emplacement et le nom de vos chiers (qui seront par exemples vos programmes en C++).
Il est important de savoir que les chiers sont classØs dans l’ordinateur (sur le disque magnØtique) selon une structure arborescente. Chaque noeud de l’arborescence s’appelle un rØpertoire ou dossier ou directory (en anglais). Dans ces rØpertoires se trouvent des chiers (ou le en anglais).
Par exemple signi e que le chier se trouve dans le rØpertoire aaal qui se trouve lui mŒme dans le rØpertoire phyliphrec, qui se trouve lui mŒme dans
1.2.1.1 Les rØpertoires
Pour a cher la liste des chiers et des sous rØpertoires du rØpertoire courant :
ls ou ls -als (pour avoir plus de dØtails) Pour changer de rØpertoire : pour aller dans le sous rØpertoire appelØ rep : cd rep pour aller dans le rØpertoire parent : cd ..
pour revenir votre rØpertoire d’origine : cd ou cd ~ Pour crØer un sous rØpertoire appelØ rep : mkdir rep
Pour dØtruire un sous rØpertoire appelØ rep : rm -r rep
1.2.1.2 Exercices sur les rØpertoires
A l’aide des commandes expliquØes ci-dessus :
1. Placez vous dans votre rØpertoire d’origine : cd
2. VØri ez que vous y Œtes bien : pwd
3. Regardez la liste des chiers par : ls , puis avec : ls -als
4. CrØer un rØpertoire du nom : essai VØri er son existence (avec ls).
5. Aller dans ce rØpertoire : cd essai VØri er que vous y Œtes (avec pwd).
6. Revenir dans le rØpertoire d’origine par cd .. VØri ez que vous y etes. DØtruire le rØpertoire essai
1.2.1.3 Les chiers (en exercice)
CrØer un nouveau chier avec l’Øditeur emacs : emacs & Øcrire le texte : salut
Menu : File/Save
Menu : File/Quit
On vØri e le contenu du chier dans la fenŒtre de commandes : more Pour changer le nom du en : mv Pour dØplacer le dans le sous rØpertoire rep :
mkdir rep
mv
Pour copier le dans le rØpertoire parent :
cd rep
cp Pour dØtruire le(s) chiers :
rm cd ..
rm rm -r rep
1.2.1.4 La documentation sur les commandes unix
Pour avoir la documentation sur la commande ls par exemple, taper dans une fenetre de commande : man ls
Voici une documentation sur les commandes possibles bash pour la fenŒtre terminal.
1.2.2 Programmer avec l’Øditeur emacs et une fenŒtre de com-
mandes
2. Ecrire la commande emacs & qui lance l’Øditeur. Le signe & signi e que le terminal reste utilisable. Dans emacs, une fois pour toutes, cocher dans le menu Options/UseCUAKeys et Options/SaveOptions qui vous permettra d’utiliser les raccourcis clavier C-c C-v pour copier/coller .
3. Dans emacs, ouvrir un nouveau chier qui sera vide la premi?re fois (pour faire cela : Menu/File/Visit New File et ecrire , Valider). Le su xe .cc signi e qu’il contiendra du code C++ (ou peut aussi choisir le su xe .cpp). Dans ce chier, Copier/coller le code suivant :
#include <iostream> using namespace std; int main() {
cout << "Bonjour!" << endl;
}
et sauvegarder ce chier qui contient le code C++ de votre programme. (Observer les raccourcis clavier proposØs par emacs dans ses menus pour toutes ces commandes : par exemple Save : C-x C-s signi e que les touches Ctrl avec x puis Ctrl avec s. Shell Command M-! signi e les touches Alt avec!).
4. Dans emacs ouvrir un nouveau chier Makefile qui sera vide la premi?re fois. Dans ce chierCopier/coller le code de compilation suivant :
all: g++ -o projet1 -std=c++11 -O3 -Wall -lm
(1.1)
et sauvegarder ce chier qui contient les instructions pour compiler votre programme. Attention : au dØbut de la deuxi?me ligne rajouter un espace de tabulation avant g++. Remarque : on peut basculer d’un chier l’autre dans emacs par le menu Bu ers ou par le clavier avec C-x C-?.
5. Compiler le programme par la commande du menu : Tools/Compile.. qui fait apparaitre en bas de page : make -k que l’on remplace par make all (puis faire entrØe). Si tout se passe bien le dernier message est compilation nished .
7. Dans le terminal, placez vous dans le rØpertoire qui contient votre projet, vØri ez avec la commande ls qu’il contient le chier exØcutable projet1 , et exØcutez ce chier en Øcrivant : ./projet1
(remarque : ./ signi e le rØpertoire prØsent)
Exercice 1.2.1.
1. Dans le programme ajoutez une erreur, par exemple en enlevant un signe; en n de ligne. Recompiler le programme et observer que le compilateur dØtecte l’erreur et en cliquant sur le message d’erreur, emacs vous place la ligne oø est l’erreur corriger.
2. Dans les instructions ci-dessus nous vous avons proposØ d’utiliser un chier Make le qui sera surtout utile pour les projets de programmation en C++. Vous pouvez plus directement compiler votre programme en Øcrivant dans un terminal la commande de compilation :
g++ -o projet1 -std=c++11 -O3 -Wall -lm
le dØsavantage est que pour corriger l’erreur il faut soit mŒme se placer sur la ligne indiquØe.
Une autre possibilitØ pour compiler est d’Øcrire la commande make all dans le terminal qui a pour e et d’exØcuter le chier Makefile.
3. emacs peut mettre votre programme en forme : pour cela sØlectionnez tout le texte et appuyez sur la touche tabulation. Il appara t des dØcalages de lignes convenables, mais non obligatoires.
4. Remarque : pour Øcrire le programme, la place de emacs vous pouvez utiliser tout autre Øditeur de texte.
1.2.2.1 ParamØtrage de emacs
Voici quelques paramØtrages utiles pour la suite :
En haut du chier .cc rajouter la ligne suivante : // -*- mode:C++; compile-command: "make all" -*-
Cela a pour e et de prØciser la commande de compilation lorsque l’on fait Menu/Tools/Compile
On peut aussi remplacer "make all" par "make all; projet1;" ce qui a pour e et de compiler et de lancer l’exØcutable projet1 Dans le chier ~/.emacs rajouter la ligne : (global-set-key (kbd "<f9>") ’compile)
Autres options, voir developper en c++ sous emacs.
Premi?re partie
Bases du langage C++
18
Chapitre 2
Le tout dØbut. Lire le clavier et a cher l’Øcran
2.1 A chage dans un terminal
Voici un petit programme en C++ qui additionne deux nombres entiers a et b , a ecte le rØsultat dans c et a che le rØsultat l’Øcran.
#include <iostream> using namespace std;
/* ==============
Programme principal ===================*/ int main()
{ int a,b; // dØclaration des objets a et b de la classe int a=1; // affectation: a prend la valeur 1 b=a+1; // affectation b prend la valeur 2 int c; // dØclaration de l objet c de classe int
c=a+b; // affectation: c prend la valeur a+b c’est dire 3
// affichage l’Øcran:
cout << "la somme de " << a << " et " << b << " vaut "
<< c <<endl;
}
Exercice 2.1.1. Recopiez ce programme dans un chier , modi er la commande de compilation de Makefile pour le compiler et exØcutez le.
19
CHAPITRE 2. LE TOUT D BUT. LIRE LE CLAVIER ET AFFICHER L’ CRAN20
RØsultat du programme : la somme de 1 et 2 vaut 3
Remarque 2.1.2. sur la syntaxe du programme :
1. La classe int permet justement de stocker des nombres entiers. (Cela vient de integer en anglais).
2. Le point virgule sØpare les instructions.
3. Les commentaires doivent dØbuter par //. Le reste de la ligne est alors considØrØ comme un commentaire. Un commentaire n’a aucune in uence sur le programme, il sert en gØnØral expliquer ce que fait le programme pour le rendre comprØhensible au lecteur. Une autre possibilitØ est de commencer le commentaire par /* et de nir quelques lignes apr?s par */.
5. "main " veut dire "principal " en anglais. C’est le dØbut du programme principal ou de la fonction principale. Les parenth?ses () signi ent que le programme principal n’utilise aucun param?tre dans cet exemple. Lorsque vous lancez votre programme, son exØcution commence toujours par la premi?re ligne de cette fonction principale qui s’appelle toujours main.
Remarque 2.1.3. sur l’a chage :
1. Pour l’a chage de texte dans un terminal, on utilise l’objet cout. Cet objet cout reprØsente le terminal. Les signes << sont Øvocateurs : on envoit ce qui suit vers l’Øcran. On peut enchainer les objets que l’on envoit l’Øcran en Øcrivant sous la forme : cout << A<<B<<C<<D; . Le symbole endl signi e que l’on va la ligne (end of line = n de ligne).
2. Les caract?res entre guillements " " sont considØrØs comme une cha ne de caract?res et Øcrits tels quels l’Øcran . Par contre les caract?res a, b,c ne sont pas entre . Cela signi e que ce sont des noms d’objets, et qu’il faut a cher le contenu de ces objets (et non pas leur nom).
CHAPITRE 2. LE TOUT D BUT. LIRE LE CLAVIER ET AFFICHER L’ CRAN
Exercice 2.1.4. tabulation (*) "\t" permet d’a cher une tabulation (i.e. sauter un espace jusqu’ la colonne suivante). Modi er le programme prØcØdent pour a cher :
objets: a b c=a+b valeurs: 1 2 3
Remarque 2.1.5. Voici la liste des autres caract?res spØciaux comme la tabulation \t , le retour la ligne suivante \n , le retour au dØbut de ligne \r , le caract?re \ lui mŒme par \\ , etc.
2.2 Lire le clavier
Il peut Œtre intØressant que l’utilisateur puisse lui-mŒme entrer les valeurs de a et b. Pour cela l’ordinateur doit attendre que l’utilisateur entre les donnØes au clavier et les valide par la touche entrØe.
#include <iostream> using namespace std;
int main()
{ int a,b; // dØclaration des objets a et b de la classe int cout <<"Quelle est la valeur de a? "<<flush; cin >> a; // lire a au clavier, attendre return cout <<"Quelle est la valeur de b? "<<flush; cin >> b; // entrer b au clavier puis return int c; // dØclaration de la objet c de la classe int c = a + b; // affectation: c prend la valeur a+b
// affichage l’Øcran: cout <<"la somme de "<<a<<" et "<<b<<" vaut "<<c<<endl;
}
Exercice 2.2.1. Recopier ce programme et exØcuter le.
Remarque 2.2.2.
1. L’objet cin appartient la classe istream et reprØsente le clavier. Le signe >> est un opØrateur associØ cet objet et qui a pour e et de transfØrer les donnØes tapØes au clavier dans l’objet qui suit (une fois la touche entrØe enfoncØe).
Chapitre 3
DØclaration et a ectation des objets
3.1 Objets de base
3.1.1 Les dØclarations d’objets de base
dans un programme, pour stocker une information, on utilise un objet qui est symbolisØ par une lettre ou un mot. (Comme a,b,c prØcØdement). On choisit la classe de cet objet, selon la nature de l’information que l’on veut stocker (nombre entier, nombre virgule, nombre complexe, ou sØrie de lettres, ou matrice, etc).
Voici quelques classes de base qui existent en C++. Il y en a d’autres, voir :
DØclaration : classe objet; | signi cation | Limites | |
int a; | nombre entier | -2147483648 | 2147483647 |
double d; | nombre rØel double prØcision | ±10±308 | 10?9 pr?s |
char e; | caract?re | ||
bool v; | boolØen : vrai (true) ou faux (false) | ||
char * t; | cha ne de caract?res |
Remarque 3.1.1. Les classes ci-dessus sont les classes de base standard du langage C++ qui existent aussi en langage C. Dans le vocabulaire du C, on dirait plutot type la place de classe, et variable la place de objet. Par exemple en Øcrivant double d; on dirait que d est une variable du type double.
3.1.2 Initialisation d’un objet de base
On peut dØclarer un objet et l’initialiser en mŒme temps de di Ørentes mani?res. Voici un exemple.
#include <iostream>
22
using namespace std;
main ()
{
int i = 0; double x = 1.23;
double x1(3.4),x2(6); // equivalent a x1=3.4
double y(x1), y2 = x2; char Mon_caractere_prefere = ’X’; const char * texte = "que dire de plus?";
char c = texte[0]; // premier caractere auto z = x + 2;
{ double x=5;
}
}
3.1.2.1 Remarques
1. i est un int qui vaut 0, x un oat qui vaut 1.23, x1 est un double qui vaut 3.4, etc..
2. Le terme auto signi e que le compilateur devine lui mŒme la classe de l’objet, ici z est un double.
4. On peut initialiser un objet avec des paranth?ses comme x1(3.4). C’est Øquivalent que d’Øcrire x1=3.4. On peut initialiser un objet avec un objet dØj existant comme y(x1) ou y2 = x2.
5. Un caract?re est entre le signe ’, comme ’X’.
6. Un texte (cha ne de caract?re) est entre le signe ". Ici l’objet texte est du type const char * qui signi e que c’est une chaine de caract?res (ou tableau de caract?res) constante. Les indices du tableau commencent 0. C’est pourquoi texte[0] extrait le premier caract?re qui est q. On expliquera plus tard que texte est en fait un pointeur sur charact?res.
7. A la derni?re ligne on dØclare x = 5 dans un bloc d’accolades ou bloc d’instructions {..}. Cela est possible et signi e que dans ce bloc x est une nouvelle variable locale (qui masque la valeur de x prØcØdente).
8. On pourra bien sßr modi er ces valeurs dans la suite du programme.
Exercice 3.1.2. accolades (*) Recopiez le programme, modi ez le pour qu’il a che la valeur de chaque objet, et exØcuter le. (si auto signale une erreur, vØri ez si l’option
c++11 est cochØe, voir remarque de l’exercice 10.1.2(4).) A cher la valeur de x dans le bloc d’accolades qui contient double x=5; et apr?s ce bloc d’accolades. Dans un deuxi?me temps, essayer de ne pas mettre d’accolades et comprendre l’erreur de compilation.
3.1.3 A chage des nombres avec une prØcision donnØe
(il faut inclure <iomanip> et <math.h>)
#include <iostream> using namespace std; #include <math.h> #include <iomanip>
//
main ()
{ double c = M_PI;
cout<<setprecision(3)<<c<<endl; }
RØsultat :
3.14
RØfØrence : voir setprecision.
3.1.4 Attention la division Euclidienne
Voici un exemple :
#include <iostream> using namespace std; // main ()
{ int a=7, b=2; double c=7.;
cout<<"7./2 = "<<c/b<<" = "<<7./2<<" = "<<(double)a/b<<endl;
cout<<"Le quotient de 7/2 = "<<7/2<<" Le reste de 7/2 est r= "<<7%2<<endl;
}
RØsultat :
7/2 = 3 = 3
7./2 = 3.5 = 3.5 = 3.5
Le quotient de 7/2 = 3 Le reste de 7/2 est r= 1
Remarques :
L’exemple prØcØdent montre que l’Øcriture 7/2 signi e le quotient de la division euclidenne qui est bien 3 car 7=2*3+1. Le reste 1 euclidien est obtenu par l’opØration 7%2 qui signi e 7 modulo 2.
Pour e ectuer la division parmi les nombres rØels ( virgule) il faut utiliser le type double.
L’instruction (double)a permet de convertir la variable a qui est de classe int en une variable de classe double. Ce changement de classe est appelØ cast. Attention, cette convention que 7/2 est la division euclidienne en langage C et C++ est souvent source d’erreur.
3.1.5 Pour gØnØrer un nombre entier p alØatoire
#include <iostream> using namespace std;
#include <time.h> #include <stdlib.h>
main ()
{ srand(time(NULL)); // initialise le hasard.(faire cela une seule
fois en debut de programme) int N = 100; int p = rand()%(N+1); // -> entier alØatoire entre 0 et N compris.
cout<<" p="<<p<<endl;
}
Remarque 3.1.3. rand() gØn?re un nombre entier alØatoire entre 0 et 32768. Ensuite % signi e modulo, a%b est le reste de la division de a par b. Ainsi rand()%(N+1) est le reste de la division du nombre alØatoire par N+1. C’est donc un entier compris entre 0 et N (inclus). En c++11 il y a une classe plus ØlaborØe pour gØnØrer des nombres alØatoires selon diverses lois.
3.2 Objets plus ØlaborØs de la classe standard
3.2.1 Chaines de caract?res (string)
RØfØrence : string. Les chaines de caract?res permettent de manipuler des suites de charact?res, comme des phrases.
Voici un exemple de programme que vous pouvez essayer.
#include <iostream> #include <string> using namespace std; main ()
{ string texte1 = "J’aime le cafØ";
cout << texte1<<endl;
string texte2 = texte1 + " sans sucre";
cout << texte2<<endl; cout<<"Le premier caract?re est du texte prØcØdent est: "<<texte2[0]<<endl;
}
RØsultat :
J’aime le cafØ
J’aime le cafØ sans sucre
Le premier caract?re du texte prØcØdent est:J
Remarque 3.2.1. L’indice du premier caract?re est 0. (En informatique on numØrote souvent partir de 0). Voici plus d’informations et d’exemples sur la classe string.
Exercice 3.2.2. texte (*) Modi er le programme prØcØdent pour qu’il a che :
J’aime le cafØ sans sucre
Le troisi?me caract?re du texte prØcØdent est:a
3.2.1.1 Conversion de chi res en chaine de caract?re et inversement. Solution simple.
Avec les fonctions stoi (penser String to Integer ) stod (penser String to Double ) et to_string. On souhaite par exemple construire une chaine de caract?res qui contient le rØsultat d’une opØration numØrique.
#include <iostream> using namespace std; #include <string> #include <sstream>
// .. main ()
{
// .. Conversion number to string.
int d=7; // chiffre string S = to_string(d); cout<<"d="<<d<<" S="<<S<<endl;
// .. Conversion string to number.
string s5 = "3"; // chaine contenant des chiffres
int i5 = stoi(s5);
string s6 = "3.14"; // chaine contenant des chiffres double d6 = stod(s6); cout<<"i5="<<i5<<" d6="<<d6<<endl;
}
RØsultat : d=7 S=7 i5=3 d6=3.14
3.2.2 Nombres complexes
#include <iostream> using namespace std; #include <complex> typedef complex<double> Complex;
main () {
Complex I(0,1); // on dØfinit le nombre I
cout<<"I.I="<<I*I<<endl;
Complex a=2.+I*4., b=3.-2.*I; Complex c=a+b, d=a*b; cout<<" a="<<a<<" b="<<b<<endl; cout<<" a+b="<<c<<" a*b="<<d<<endl; cout<<" real(a)="<<a.real()<<" norme(a)="<<abs(a)<<endl;
} RØsultat :
I.I=(-1,0) a=(2,4) b=(3,-2) a+b=(5,2) a*b=(14,8) real(a)=2 norme(a)=4.47214
Remarque 3.2.3.
L’Øcriture 2. est Øquivalente 2.0 ou 2. Ainsi a=2.+I*4. est Øquivalente
a=2+4*I
typedef complex<double> Complex; signi e que le mot Complex est un raccourci pour le type complex<double> qui est un nombre complexe avec double en partie rØelle et imaginaire.
3.2.3 Liste d’objets (ou tableau de dimension 1) : vector
La classe string ci-dessus est une liste de caract?res. Plus gØnØralement la classe vector permet de manipuler des listes d’objets quelconques (des nombres ou autres). Voici les informations sur la classe vector.
Par exemple pour une liste de nombres entiers avec vector<int> :
#include <iostream> using namespace std; #include <vector>
main () {
vector<int> L; // liste vide qui contiendra des objets de type int
L.push_back(2); //rajoute l’element 2 a la fin de la liste
L.push_back(3); // rajoute 3 a la fin de la liste L.push_back(5);
cout<<"La liste a la taille :"<<L.size()<<endl; cout<<"Ses elements sont: "<<L[0]<<" , "<<L[1]<<" , "<<L[2]<<endl; } RØsultat :
La liste a la taille :3
Ses elements sont: 2 , 3 , 5
Remarque 3.2.4. Les indices de la liste commencent 0.
Autre exemple :
main () {
//--- recherche la position de l’element maximal de la liste vector<int> L={5,7,3,12,34,6}; // on initialise une liste d’entiers cout<<"Le premier ØlØment de la liste est "<<L[0]<<endl;
int p = max_element(L.begin(),L.end())-L.begin(); //renvoit la position
du maximum dans l’ordre (0,1,2..) cout<<"L’Ølement le plus grand est "<<L[p]<<endl; cout<<"Il est la position "<<p<<endl;
//--- cherche la premiere/derniere position d’un element avec find()
/ rfind()
int e = 6;
int p2 = find(L.begin(), L.end(), e) - L.begin(); // cherche la
position de l’element e if(p2<L.size())
cout<<"Le "<<e<<" est la position "<<p2<<endl; else
cout<<"Le "<<e<<" n’est pas dans la liste."<<endl; }
RØsultat :
Le premier ØlØment de la liste est 5
L’Ølement le plus grand est 34
Il est la position 4
Le 6 est la position 5
Remarque 3.2.5. Dans l’exemple ci-dessus, l’objet i qui reprØsente une position sur la liste est de la classe vector<int>::iterator. On utilise ici le terme auto qui dØtecte cette classe et est plus simple Øcrire. Voici plus d’informations et d’exemples sur la classe Pour plus d’informations et d’exemples sur les manipulations de listes (appelØes containers) voir Reference sur max_element.
3.3 Vecteurs et matrices pour l’alg?bre linØaire avec la librairie Armadillo
Pour cela on utilise la librairie armadillo dont voici la
Exemple :
#include <iostream> using namespace std; #include <armadillo> using namespace arma;
main ()
{
Col<int> V("1 2 3"); // vecteur colonne d’entiers cout<<"V="<<endl<<V<<endl;
Mat<int> M("3 2 1; 4 5 0");// matrice d’entiers
cout<<"M="<<endl<<M<<endl;
Col<int> W = M * V; // calcule le produit
cout<<"M*V="<<endl<<W<<endl;
cout<<"element de matrice M(0,0)="<<M(0,0)<<endl; }
RØsultat :
V= 1
2
3
M= 3 2 1
4 5 0
M*V= 10
14
element de matrice M(0,0)=3 Remarque 3.3.1. Vous pourrez de la mŒme fa on utiliser d’autres classes selon vos besoins condition cette fois ci d’inclure le chier d’entŒte #include < > appropriØ au dØbut
du programme et d’associer les librairies correspondantes la compilation.
Exercice 3.3.2. Recopiez ce programme et exØcuter le.
Exercice 3.3.3. matrices (*) Modi ez le programme prØcØdent pour qu’il calcule et a che :
A = 1 1
0 1
B = 1 0
1 1
A*B = 2 1
1 1
3.3.1 Quelques fonctions utiles sur les vecteurs et matrices
Veuillez consulter la page documentation pour toutes les fonctionnalitØs de armadillo sur les vecteurs et matrices.
Voici quelques exemples utiles.
#include <iostream> using namespace std; #include <armadillo> using namespace arma;
main ()
{
//---- initialisations
// vec est equivalent a Col<double>
vec V1 = randu<vec>(5); // vecteur colonne de 10 composantes aleatoires
unif. dans [0,1] cout<<"V1="<<endl<<V1<<endl;
// mat est equivalent a Mat<double> mat M1;
M1.zeros(3,3); // matrice 3*3 remplie de zeros cout<<"M1="<<endl<<M1<<endl;
//----- conversions de types
vector<double> V2 = conv_to<vector<double>>::from(V1); // V1 ->
RØsultat
V1=
0.7868 0.2505 0.7107 0.9467
0.0193
M1= 0 0 0
0 0 0
0 0 0
V2[0]=0.786821
3.4 Objets graphiques avec la librairie Root
Comme il est tr?s utile et agrØable de reprØsenter des donnØes et des rØsultats de fa on graphique, nous prØsentons tout de suite l’utilisation d’une librairie graphique. La librairie root est dØveloppØe au (Laboratoire international de physique des particules). C’est une librairie en C++ gratuite et tr?s performante, qui permet :
de faire du graphisme ØvoluØ (dessiner des axes, des courbes, des surfaces, des objets en 3D,..), mais aussi du graphisme simple (lignes, points, ronds,..) de traiter des donnØes pour faire des statistiques; cela est tr?s utile au CERN pour Øtudier les milliards de rØsultats issus d’une expØrience de collisions entre particules.
de faire des interfaces graphiques pour un programme (gestion de la souris, menus
dØroulants, boites de dialogues avec boutons, ) et beaucoup d’autres choses. Voir la page web de presentation.
Sur le rØseau il y a une documentation compl?te. Il y a aussi Documentation gØnØrale. On peut aussi accØder la Il y a une mailling list d’utilisateurs qui s’entraident. Quand on a un probl?me, il su t d’envoyer un mail, la rØponse nous revient quelques minutes ou heures plus tard. Le plus simple est souvent d’Øcrire dans le moteur de recherche de google des mots clefs comme root cern ellipse pour trouver comment dessiner une ellipse avec root.
Si root n’est pas installØ sur votre ordinateur, pour l’installer sous (X)Ubuntu, il faut Øcrire dans un terminal :
sudo apt-get install -y xorg-dev sudo apt-get install x-dev sudo apt-get install root-system
Remarque 3.4.1. Root peut s’utiliser aussi en mode interprØtØ C++ : pour cela, lancer la commande root dans un terminal, et suivre les instructions
3.4.1 Commande de compilation
-I/usr/include/root ‘root-config --cflags‘ ‘root-config --libs‘
‘root-config --glibs‘
Si vous utilisez l’Øditeur codeblocks
Dans Settings/Compiler/Compiler_Settings/Other_Options, rajouter :
-I/usr/include/root
Dans Settings/Compiler/Linker_Settings/Other_linker_Options, rajouter :
‘root-config --cflags‘ ‘root-config --libs‘ ‘root-config --glibs‘
3.4.2 Exemple de dØpart qui dessine un cercle
#include <TApplication.h> // (A)
#include <TCanvas.h> //(B)
#include <TEllipse.h> // (C)
//------ fonction main (A) -------------------int main()
{
TApplication theApp("App", nullptr, nullptr); // (A)
TCanvas c("c","fenetre",400,400); // (B) objet fenetre graphique.
Taille en pixels
c.Range(0,0,5,5); // coordonnees de la fenetre c: x:0-5, y:0-5
(optionnel, par defaut ce sera 0-1)
//------ dessin d’une ellipse dans la fenetre c (C) -------
TEllipse e(2,3,1); // on precise le centre (x=2,y=3) et le rayon=1
e.Draw(); // dessine l’ellipse
c.Update(); //(B) Montre le dessin
(); // (A) garde la fenetre ouverte et permet a l’utilisateur
d’interagir.
}
]RØsultat du programme :
Remarque 3.4.2. Le programme prØcØdent est dØcoupØ en trois types de blocs :
(A) : dans tout programme utilisant Root, ces parties doivent toujours ŒtrerØØcrites. Il y a l’objet theApp de la classe TApplication.
(B) : c’est le code qu’il faut pour crØer la fenetre graphique (objet c de la Classe TCanvas). Si votre programme fait du graphisme, il faut toujours crØer une fenŒtre graphique qui contiendra votre dessin.
(C) : c’est le code qu’il faut pour crØer le cercle (objet e de la Classe TEllipse).
Exercice 3.4.3.
Recopier le programme ci-dessus, le compiler et l’exØcuter. Une fois que le dessin apparait, vous pouvez choisir dans le menu : View/Event Status Bar qui a che la position de la souris.
Remarque 3.4.4. la commande e.Draw() appelle la fonction membre Draw() de la classe TEllipse qui dessine l’objet e. Pour connaitre toutes les opØrations possibles que l’on peut e ectuer sur l’ellipse, il faut regarder la liste des fonctions membres de cette classe TEllipse. On remarquera dans l’entŒte, que la classe TEllipse hØrite d’autres classes comme la classe TAttLine qui concerne les propriØtØs des traits. Ainsi la classe TEllipse peut utiliser les fonctions de la classe TAttLine comme la fonction SetLineColor() qui permet de changer la couleur des traits.
Exercice 3.4.5. Modi er le programme ci-dessus pour dessiner une ellipse remplie d’un motif bleu comme ci-dessous. Aide : d’apr?s la documentation, hØrite de la classe On utilisera les fonctions membres :
e.SetFillColor(kBlue);
e.SetFillStyle(3025); // motif en carreaux
3.4.3 Quelques objets graphiques
Voici quelques exemples de graphisme 2D. Remarquez que au dØbut du programme on ajoute une ligne spØci que par exemple #include <TLine.h> si on utilise la classe TLine.
#include <TApplication.h>
#include <TCanvas.h>
#include <TLine.h>
#include <TEllipse.h>
#include <TMarker.h>
#include <TBox.h> #include <TText.h>
int main()
{
TApplication theApp("App", nullptr, nullptr);
//.. La fenetre
TCanvas c("titre","titre",10,10,400,400); //position x,y sur l’ecran et taille X,Y en pixels
c.Range(0,0,1,1); // xmin,ymin,xmax,ymax, systeme de coordonnees
//.. une ligne bleue
TLine l(0.1,0.5,0.3,0.9); // x1,y1,x2,y2. l.SetLineColor(kBlue); // bleu
l.Draw();
//.. Une Ellipse (ou cercle)
TEllipse e(0.2,0.3,0.1); // (centre (x,y) et rayon r
e.Draw();
//.. Un point
TMarker m(0.3,0.4,8); // (x,y) et 6 ou 8: forme du Marker m.SetMarkerColor(kPink);
m.Draw();
//.. Un rectangle vert
TBox p(0.15,0.6,0.25,0.7); // on precise les coins bas-gauche (x1,y1) et haut droit (x2,y2) :
p.Draw();
//.. Du texte
TText texte(0.2,0.2,"un petit texte"); // position x,y ();
c.Update(); // Montre le dessin
(); // garde la fenetre ouverte et permet a l’utilisateur
d’interagir.
}
3.5 SupplØments
3.5.1 Continuer une ligne de c++ la ligne suivante avec \
#include <iostream> using namespace std;
main () {
cout<< abcdefghij <<endl;
cout<< abc\ def\ ghij \
<<endl;
} rØsultat : abcdefghij abcdefghij
3.5.2 Sur les types
3.5.2.1 Renommage
Dans la dØclaration vector<int> L; on dit que l’objet L est du type vector<int>. Parfois un type peut Œtre long Øcrire. Pour simpli er les notations, voici deux commandes qui sont Øquivalentes et qui ont pour e et de dØ nir le nouveau type C comme Øtant Øquivalent au type vector<int> (dØj existant). Reference.
#include <vector> using C = vector<int>; typedef vector<int> C;
3.5.2.2 Connaitre le type pour connaitre le type de la variable a on peut utiliser typeid(a).name() et Øcrire
#include <iostream> using namespace std; #include <typeinfo>
main () { auto a=’A’; cout << typeid(a).name() <<endl; auto x=3.14;
cout << typeid(x).name() <<endl; auto s="ABC";
cout << typeid(s).name() <<endl;
} rØsultat :
c d PKc
Dans ce rØsultat, c signi e caract?re, d signi e double et PKc signi e chaine de caract?res.
3.5.3 Quelques opØrations ØlØmentaires
#include <iostream> using namespace std;
main ()
{ int b=1; cout<<" b="<<b<<endl; b++; // equivalent a b=b+1 cout<<" b="<<b<<endl; b--; // equivalent a b=b-1 cout<<" b="<<b<<endl; }
rØsultat b=1 b=2 b=1
3.5.4 Sur les chaines de caract?res
RØfØrence : string.
#include <iostream> using namespace std; #include <string> int main() {
string s1 = "ab&-e&-f";
string s2 = s1.substr(0, ("&-")); // extrait sous chaine
entre le debut (indice 0) la premiere occurence de "&-" string s3 = s1.substr(0, s1.rfind("&-")); // extrait sous chaine
entre le debut (indice 0) et la derniere occurence de "&-" string s7 = s1.substr(("&-")); // extrait sous chaine entre
la premiere occurence de "&-" et la fin
string s4 = s1; // copie
s4.erase(0,2); // a la position 0, enleve 2 caracteres string s5 = s1; // copie
s5.erase(()-2,2); // enleve les 2 caracteres de la fin
string s6 = s1; // copie int p;
while(((("-"))>=0) && (p<())) s6.replace(p,1,"*"); // remplace tous les "-" par "*" cout<<"s1="<<s1<<endl
<<"s2="<<s2<<endl
<<"s3="<<s3<<endl
<<"s7="<<s7<<endl
<<"s4="<<s4<<endl
<<"s5="<<s5<<endl
<<"s6="<<s6<<endl;
}
RØsultat :
s1=ab&-e&-f s2=ab s3=ab&-e s7=&-e&-f s4=&-e&-f s5=ab&-e& s6=ab&*e&*f
3.5.4.2 Conversion de chi res en chaine de caract?re et inversement. Solution sophistiquØe (permet des mØlanges de type)
On a dØj un moyen ØlØmentaire en Section 3.2.1.1.
On souhaite par exemple construire une chaine de caract?re s qui contient le resultat d’une opØration numerique. Pour cela il faut utiliser la classe ostringstream. Inversement pour extraire des chi res d’une chaine de caract?re il faut utiliser la classe istringstream .
#include <iostream> using namespace std; #include <string> #include <sstream>
// .. main ()
{
// .. Conversion chiffre -> string.
int c=7; // chiffre
ostringstream os;
// .. Conversion string -> chiffres.
string s2 = "3 4"; // chaine contenant des chiffres istringstream is;
(s2); // convertit chaine s2 en is int a,b;
is>>a; // extrait le premier chiffre trouve dans is is>>b; // extrait chiffre de is cout<<"s2="<<s2<<endl;
cout<<"a="<<a<<" b="<<b<<endl; // affichage pour verifier }
d=7 S=7 s= resultat :7 s2=3 4 a=3 b=4
3.5.4.3 Conversion nombre <-> chaine de caractere qui reprØsente le nombre en base 2
Avec la classe bitset
#include <iostream> using namespace std; #include <string>
#include <bitset> // .. main () {
// Conversion entier -> string (base 2) int n4=117;
string s4 = bitset<8>(n4).to_string(); // nombre -> string (base
2) cout<<"n4="<<n4<<" s4="<<s4<<endl;
//.. Conversion string (nombre en base 2) -> entier
string s5="10011";
unsigned long n5 = bitset<8>(s5).to_ulong(); cout<<"s5="<<s5<<" n5="<<n5<<endl;
}
Resultat :
n4=117 s4=01110101 s5=10011 n5=19
3.5.4.4 Conversion de string vers char*
Il est parfois utile d’obtenir la chaine sous le format char*. Le faire avec c_str().
3.5.5 Quelques opØrations en base 2 (binaire, les bits) et en base
16 (hexadecimale)
Avec la classe bitset
#include <iostream> using namespace std; #include <bitset> main ()
{
// Declaration et Affichage en base deux:
int A=0b1001; // declaration en base 2 (prefixe 0b) cout<<" A="<<A<<" en binaire = "<<bitset<8>(A)<<endl; cout<<" Nombre de bits non nuls de A="<< bitset<8>(A).count()<<endl;
//.. decalages de bits
//.. operations bits a bits
int B = 0b1;
int d = A | B; // ou inclusif bit a bit cad 1|1 donne 1 int e = A ^ B; // ou exclusif bit a bit cad 1^1 donne 0
int f = A & B; // et bit a bit int g = ~A; // non A, inversion des bits cout<<" B="<<B<<" = "<<bitset<4>(B)<<endl; cout<<" A|B = "<<d<<" = "<<bitset<4>(d)<<endl; cout<<" A^B = "<<e<<" = "<<bitset<4>(e)<<endl; cout<<" A&B = "<<f<<" = "<<bitset<4>(f)<<endl; cout<<" ~A = "<<g<<" = "<<bitset<8>(g)<<endl; }
Resultat :
A=9 en binaire = 00001001 Nombre de bits non nuls de A=2 b =144 = 10010000 c =18 = 00010010 B=1 = 0001
A|B = 9 = 1001
A^B = 8 = 1000
A&B = 1 = 0001
~A = -10 = 11110110
Remarque 3.5.1. De mŒme on peut utiliser la base 16 (hexadØcimale) avec le prØ xe 0x.
Par exemple :
a=0xFA; cout<<a<<endl; cout<<hex<<a<<endl; // pour a cher en base 16.
3.5.6 tuple : ensemble d’objets divers
Un objet de la classe tuple est un ensemble d’objets divers. Par exemple :
#include <iostream> using namespace std; #include <tuple> main ()
{
// construction
tuple<int,char> o1(10,’x’); // creation d’un objet constituØ d’un
entier 10 et un caract?re x auto o2 = make_tuple ("test", 3.1, 14, ’y’); // construction sans
preciser les types
// lecture des elements
int a = get<0>(o1); // on extrait le premier element (position 0)
cout<<"a="<<a<<endl;
int b; char c;
tie (b,c) = o1; // extraits les elements de o1
cout<<"b="<<b<<" c="<<c<<endl;
int d;
tie (ignore, ignore, d, ignore) = o2; // extrait certains elements
de o2
cout<<"d="<<d<<endl;
// .. ecriture d’elements
}
RØsultat :
a=10 b=10 c=x d=14 o1[0]=100 o3[5]=y
3.5.7 Vecteurs et matrices avec la librairie armadillo
3.5.7.1 Diagonalisation d’une matrice symØtrique rØelle documentation. Exemple :
#include <iostream> using namespace std; #include <armadillo> using namespace arma; main () { int N=3;
mat M = randu<mat>(N,N); // elements au hasard M= 0.5*(M + trans(M)); // la rend symetrique
vec val_p; mat vec_p; eig_sym( val_p, vec_p, M);
cout<<"Matrice symetrique M="<<endl<<M<<endl; cout<<"valeurs propres="<<endl<<val_p<<endl; cout<<"vecteurs propres en colonnes="<<endl<<vec_p<<endl;
cout<<"Verification M v0-l0 *v0 = 0? on trouve: "<<endl<<M*(0)
- val_p(0) *(0) <<endl;
}
RØsultat :
Matrice symetrique M=
0.7868 0.5986 0.4810 0.5986 0.0193 0.2138
0.4810 0.2138 0.5206
valeurs propres=
-0.3109
0.2173
1.4203
vecteurs propres en colonnes=
0.5003 0.4080 0.7637
-0.8633 0.3037 0.4032
-0.0674 -0.8610 0.5041
Verification M v0-l0 *v0 = 0? on trouve:
-2.2204e-16
-2.2204e-16
-1.2143e-16
3.5.7.2 Diagonalisation d’une matrice hermitienne complexe documentation. Exemple :
#include <iostream> using namespace std; #include <armadillo> using namespace arma; main () { int N=3;
cx_mat M = randu<cx_mat>(N,N); // elements au hasard M= 0.5*(M + trans(M)); // la rend symetrique vec val_p;
cx_mat vec_p; eig_sym( val_p, vec_p, M);
cout<<"Matrice symetrique M="<<endl<<M<<endl; cout<<"valeurs propres="<<endl<<val_p<<endl; cout<<"vecteurs propres en colonnes="<<endl<<vec_p<<endl;
cout<<"Verification M v0-l0 *v0 = 0? on trouve: "<<endl<<M*(0)
- val_p(0) *(0) <<endl;
}
RØsultat :
Matrice symetrique M=
(+7.868e-01,+0.000e+00) (+4.810e-01,-4.620e-01) (+7.966e-02,+6.948e-02)
valeurs propres=
-0.1786
0.5407
1.4451
vecteurs propres en colonnes=
(+3.937e-01,-3.110e-01) (+4.834e-01,-2.058e-01) (+6.801e-01,-9.884e-02)
(-7.290e-01,-1.402e-01) (-1.696e-01,+6.247e-02) (+5.471e-01,+3.419e-01)
(+4.440e-01,+0.000e+00) (-8.315e-01,+0.000e+00) (+3.339e-01,-0.000e+00)
Verification M v0-l0 *v0 = 0? on trouve:
(-8.327e-17,+5.551e-17)
(-5.551e-17,+6.939e-18)
(-1.804e-16,+1.388e-17)
3.5.7.3 Diagonalisation d’une matrice complexe quelconque documentation. Exemple :
#include <iostream> using namespace std; #include <armadillo> using namespace arma; main () { int N=3;
cx_mat M = randu<cx_mat>(N,N); // elements au hasard cx_vec val_p;
cx_mat vec_p; eig_gen( val_p, vec_p, M); // diagonalise
// ordonne les valeurs propres (et vect p) par module decroissant uvec indices = sort_index(abs(val_p),"descend"); // liste des indices,
"ascend" or "descend" val_p=val_p(indices); // vecteur ordonnØ (indices); // vecteur ordonnØ
cout<<"Matrice complexe M="<<endl<<M<<endl; cout<<"valeurs propres="<<endl<<val_p<<endl; cout<<"vecteurs propres en colonnes="<<endl<<vec_p<<endl;
cout<<"Verification: M v0-l0 *v0 = 0? on trouve: "<<endl<<M*(0)
- val_p(0) *(0) <<endl;
}
Chapitre 4
Les instructions de base
4.1 La boucle "for"
L’instruction for permet de rØpØter un bloc d’instructions. Par exemple, la syntaxe de la ligne for(int i=2; i<=8; i=i+2) dans l’exemple ci-dessous signi e : Fait les instructions en partant de l’entier i=2, repŒte l’instruction i=i+2 et les instructions qui suivent dans le bloc { } tant que i<=8 (infØrieur ou Øgal) .
#include <iostream> using namespace std;
int main() {
for(int i=2; i<=8; i=i+2)
{ int j=i*i;
cout <<i << "\t "<<j <<endl;
}
}
rØsultat :
Exercice 4.1.2. Ecrire un programme qui a che les valeurs 0.1 0.2 0.3 .. jusqu’ 10.0.
48
Remarque 4.1.3. Une autre possibilitØ d’utilisation de la boucle for en C++11 est donnØ avec l’exemple suivant. Dans ce cas la syntaxe est : for(auto x:L) et signi e pour chaque objet x dans la liste L fait l’instructions qui suit .
#include <iostream> using namespace std; #include <vector>
int main()
{ vector<int> L={5,7,3,12}; // une liste for(auto x:L) // la variable x parcourt la liste L cout<<x+1<<","; } rØsultat :
6,8,4,13,
Remarque 4.1.4. Bloc d’instruction. On a vu qu’un bloc d’instructions (= un ensemble d’instructions) est dØlimitØ par des accolades : {..}. Par exemple dans une bouclefor, si vous avez une seule instruction, il n’est pas nØcessaire de mettre des accolades. Si vous avez plusieurs instructions il faut mettre des accolades. Exemple :
#include <iostream> using namespace std; int main() {
for(int i=2; i<=8; i=i+2) cout<<"i="<<i<<endl; // pas d’accolades
for(int i=2; i<=8; i=i+2) { // accolade de debut de bloc
int j=i*i; cout<<"i="<<i << "\t "<<"j="<<j <<endl;
} //accolade de fin
} rØsultat :
i=2 i=4 i=6 i=8 i=2 j=4 i=4 j=16 i=6 j=36 i=8 j=64
Exercice 4.1.5. Cercle qui tourne(*) :
En faisant une boucle sur l’angle ? qui varie entre 0 et 2? par pas de 0.01 radian, faire un programme oø l’on voit en animation un cercle de rayon r = 1, qui parcourt le cercle de rayon R = 3 dans le sens trigonomØtrique.
Aide : utiliser la librairie root, Section 3.4, et les formules de trigonomØtrie x = Rcos?, y = Rsin?. Pour utiliser les fonctions trigonomØtriques cos et sin il faut rajouter #include <math.h> en haut du programme. Le nombre ? est connu en C++ en Øcrivant M_PI
4.2 Ecrire une condition
On a utilisØ ci-dessus la condition i <= 30 qui signi e i infØrieur ou Øgal 30. Voici la syntaxe pour Øcrire d’autres conditions :
4.2.1 Les opØrateurs de comparaison :
Signi cation | symbole |
supØrieur | > |
infØrieur | < |
supØrieur ou Øgal | >= |
infØrieur ou Øgal | <= |
Øgal | == |
di Ørent de | != |
Attention la confusion possible entre == et = : a == 2 sert tester l’ØgalitØ de l’objet a avec 2 (cela ne change pas la valeur de a). Par contre a = 2 met la valeur 2 dans l’objet a.
4.2.2 Les opØrateurs logiques
Signi cation | symbole |
et | && |
ou | || |
non | ! |
Exercice 4.2.1. Que fait le programme suivant? (deviner puis ensuite essayer pour vØrier)
#include <iostream> using namespace std;
main () { int a=1, b=2, c=3; if( ((a <= b ) || (b >=c) ) && (a<c) ) cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
4.2.3 Remarque
Lorsqu’une condition est ØvaluØe comme i<=30, la valeur rendue est de la classe boolean (vrai ou faux).
4.3 La boucle do {..} while (..);
signi e fait le bloc d’instructions {..} tant que la condition (..) est vraie.
#include<iostream> using namespace std;
main( ) { int MAX(11); int i(1),j; do
{ j=i*i; cout<<i<<"\t "<<j<<endl;
i=i+1; // Ne pas oublier l’incrØmentation
} while(i<MAX); //condition }
rØsultat :
1 1
2 4
etc
10 100
4.4 La boucle while (..) {..};
signi e tant que la condition (..) est vraie, fait le bloc d’instructions {..} .
#include <iostream> using namespace std;
main ( )
{ int max=11; int i=1, j; while(i<max) // condition
{ j=i*i;
cout<<i<<"\t"<<j<<endl;
}; // ne pas oublier le point virgule }
RØsultat :
1 1
2 4 etc
10 100
4.5 L’instruction if
#include<iostream>
using namespace std;
int main() {
for(int i=1; i<=10; i=i+1)
{ if (i==4)
cout<<"i= "<<i<<endl;
}
}
RØsultat 4
Exemple plus gØnØral : On peut complØter avec if.. else if . else.. comme ceci :
#include<iostream> using namespace std; int main() {
for(int i=1; i<=10; i=i+1)
{
if (i == 4)
cout<<"i="<<i<<endl;
else if(i==5)
cout<<"2*i= "<<2*i<<endl; else
cout<<"."<<endl;
}
}
RØsultat :
.
.
.
i=4 2*i= 10
.
.
.
.
.
Exercice 4.5.1. erreurs ? (*) On souhaite a cher la suite 12345. Trouver l(es) erreur(s) et corriger le programme suivant :
#include<iostream> using namespace std;
int main() { int i=1; for(i=1; i<5; i=i+1); cout<<i;
}
4.6 break : pour sortir de la boucle. continue : pour sauter l’instruction.
#include <iostream> using namespace std; int main() {
for(int i=2;i<=10;i=i+2)
{ if (i==4) continue; // on passe au suivant. if (i==8) break; // on sort de la boucle
int j=i*i;
cout<<i << "\t "<<j <<endl;
}
}
Ce programme produira :
2 4
Exercice 4.6.1. nombre au hasard(*)
Faire un programme qui au dØpart choisit un nombre au hasard entre 0 et 1000 (Utiliser la Section 3.1.5), puis demande l’utilisateur de le trouver, en rØpondant "trop grand " ou "trop petit " chaque essai. L’utilisateur a le droit 10 essais maximum.
Chapitre 5
Les fonctions
Une fonction est un petit sous programme qui e ectue des instructions et utilise Øventuellement certains param?tres donnØs en entrØe. Une fonction peut renvoyer un objet ou ne rien renvoyer du tout.
Dans l’exemple suivant, la fonction carre calcule j=i*i. Cette fonction prend en entrØe un objet int i et renvoit double j en sortie. Pour cela on utilise la syntaxe double carre(int i).
#include<iostream> using namespace std;
//======declaration de la fonction carre ===================================
double carre(int i)
{ double j=i*i;
return j; //on renvoie le rØsultat au programme principal
}
//===declaration de la fonction principale ===== int main()
{ int x;
cout<<"entrer x "<<endl;
cin>>x;
double y=carre(x); // appel de la fonction carre cout<<"Le carrØ de "<<x<<" est "<<y<<endl;
55
} rØsultat :
entrer x
2
le carrØ de 2 est 4
Remarque 5.1.1.
1. La fonction carre renvoit son rØsultat qui est un objet de la classe double. Pour cela on utilise l’instruction return. Pour appeler cette fonction, on utilise la syntaxe y=carre(x) si bien que le rØsultat renvoyØ est tout de suite stockØ dans l’objet y. 2. Dans le bloc {..} de la fonction carre on a dØclarØ l’objet j.Par consØquent, cet objet j n’est connu que dans ce bloc et pas ailleurs. On dit que c’est un objet local. On ne peut pas l’utiliser dans la fonction main. De mŒme l’objet x dØclarØ dans le bloc de la fonction main n’est pas connu ailleurs : On ne peut pas l’utiliser dans la fonction carre.
5.2 Exemple de fonction qui ne renvoit rien
#include <iostream> using namespace std;
//!========declaration de la fonction carre============= void carre(int i)
{ int j=i*i; // declaration d’un objet local cout<<"le carrØ de "<<i<<" est "<<j<<endl;
}
//!====declaration de la fonction principale =====================
int main()
{ int x;
cout<<" entrer x "<<endl;
cin>>x;
carre(x); // appel de la fonction carre }
rØsultat :
entrer x
2
le carrØ de 2 est 4
Remarque 5.2.1.
5.3 Param?tres des fonctions par rØfØrence
Quand le programme principal fournit un objet une fonction, on peut vouloir que celle-ci modi e le contenu de l’objet. Dans l’exemple prØcØdent la procØdure ou la fonction carre ne modi e pas la valeur de x. Dans l’exemple ci-dessous, on veut Øchanger le contenu de deux objets a et b. On doit alors dire la fonction qu’elle a le droit de changer les objets qu’on lui donne en entrØe. Pour cela, dans la dØclaration des param?tres, on met le signe & devant les param?tres pouvant Œtre modi Ø par la fonction. On dit que ce sont des param?tres passØs par rØfØrence.
#include<iostream> using namespace std;
//====== fonction permute ==================
void permute(int& i,int& j) // passage des parametres par reference
{ int t=i; // stockage temporaire
i=j; j=t;
}
//======== fonction main =================== int main()
{ int a=10, b=5; cout<<a<<" "<<b<<endl; permute(a,b); cout<<a<<" "<<b<<endl;
}
rØsultat :
10 5
5 10
Exercice 5.3.1. Executer ce programme, puis enlever les signes & (de rØfØrence) dans le passage des param?tres, et rØ-essayer. Conclusion?
5.4 La surcharge des fonctions.
Un aspect interessant du C++ est que l’on peut "surcharger " les fonctions : c’est dire que l’on peut donner le mŒme nom des fonctions qui font des choses di Ørentes. Ce sont les param?tres demandØs lors de l’appel de la fonction qui permet l’ordinateur de distinguer qu’elle est la fonction appeler.
5.4.1 Exemple :
#include<iostream> using namespace std;
//------------------------------------------void f(int i)
{ cout<<"fonction 1 appelØe"<<endl; cout<<" param?tre = "<<i<<endl;
}
//------------------------------------------void f(char *s,int i)
}
//--------------------------------------------int main()
{ f(10); f("Cha ne ",4);
}
Renvoit fonction 1 appelØe param?tre = 10
fonction 2 appelØe
param?tre = Cha ne param?tre = 4
Exercice 5.4.1. factorielle (*)
Ecrire une fonction que l’on appelera factorielle qui en entrØe prend un objet x de la classe long, et en sortie renvoit sa factorielle x! de la classe long. Dans la fonction principale on utilisera cette fonction avec le code suivant :
long a=5; long b = factorielle(a); cout<<" a="<<a<<" a!="<<b<<endl; a n qu’il a che comme rØsultat : a= 5 a!=120
Remarque 5.4.2. Ici on programme la fonction factorielle; c’est un exercice. Mais cette fonction factorielle est bien sßr dØj programmØe comme d’autres fonctions spØciales que l’on trouve dans la librairie boost .
Chapitre 6
Les pointeurs
6.1 DØclaration et a ectation d’un pointeur
La notion de pointeur est importante dans le langage C et C++. Elle est rØputØe comme Øtant di cile et technique; nous espØrons que vous aurez nØanmoins les idØes claires apr?s la lecture de cette section.
Nous introduisons rapidement la notion de pointeur, et montrons comme exemple, son intØrŒt pour crØer des tableaux de taille variable au cours du programme.
Rappellons dØj ce qu’est un objet. Par exemple :
int i;
Cette instruction a pour e et de rØserver un objet en mØmoire de l’ordinateur, permettant de stocker un nombre entier. Cet objet s’appelle ’i’ son type (ou sa classe) est int.
Bien sßr, cet objet se trouve quelque part dans la mØmoire de l’ordinateur. Il a un certain emplacement, caractØrisØ par son adresse mØmoire , appellØe son pointeur.
Il faut donc retenir que pointeur d’un objet signi e adresse d’un objet dans la mØmoire de l’ordinateur.
Gr ce au signe *, la deuxi?me ligne dØclare p comme Øtant un pointeur sur entier (c’est dire une adresse d’un objet contenant un entier).
Le signe &i signi e l’adresse de l’objet i.
(*p) signi e l’objet situØ l’adresse du pointeur p.
Si fonction() est une fonction appartenant une classe et si p est un pointeur d’un objet de cette classe, alors l’instruction p->fonction(); est Øquivalente (*p).fonction();
SchØma qui rØsume ce que l’on vient d’expliquer :
60
ConsØquences : pour a cher le contenu de l’objet i on a maintenant deux possibilitØs qui sont Øquivalentes :
cout<<i;
ou :
cout<<(*p);
Pour modi er le contenu de l’objet i on a maintenant deux possibilitØs qui sont Øquivalentes :
i=5;
ou :
(*p)=5;
Remarque 6.1.1. Attention : avant d’e ectuer l’opØration d’Øcriture : (*p)=5; il faut Œtre sur que l’adresse du pointeur correspond un objet existant. Vous avez donc compris que avant d’Øcrire, il faut prendre le soin de rØserver de la place mØmoire. On parle d’allocation de la mØmoire.
On comprends l’Øcriture int *p; comme la dØclaration que *p est un objet de type int, donc p est un pointeur sur un objet de type int.
6.1.1 Param?tres de la fonction main()
Jusqu’ prØsent, nous avons Øcrit la fonction principale du programme par la dØclaration int main() { }
En fait cette fonction peut prendre des param?tres et renvoyer un entier. Cela peut Œtre utile pour passer des param?tres un programme et obtenir des param?tres en retour.
Voici un exemple que l’on commente ensuite :
#include <iostream> using namespace std;
int main(int argc, char *argv[])
{
cout<<"nombres de parametres argc ="<<argc<<endl; cout<<" Ce sont:"<<endl; for(int i=0;i<argc; i++)
}
return 0; // renvoit l’entier 0 (signifie habituellement 0K) }
ExØcution
./test 2 toto 5
RØsultat : nombres de parametres argc =4 Ce sont: argv[0] = ./test argv[1] = 2 argv[2] = toto argv[3] = 5
Commentaires On a lancØ le programme par la commande ./test 2 toto 5 qui contient en e et 4 param?tres, le premier Øtant le nom du programme lui mŒme. Cet exemple montre que au dØbut du programme main(..) , la variable argc contient le nombre de param?tres et argv est un tableau de chaines de caract?res pour chacun de ces param?tres.
6.2 Allocation dynamique de la mØmoire
On peut rØserver en mØmoire de l’ordinateur une suite de n objets de type double par l’instruction :
p=new double[n];
Apr?s cela, p pointe sur le premier objet rØservØ.
Remarque 6.2.1. De fa on similaire p=new string[13]; rØserve en mØmoire une suite de 13 objets de type string
Cela s’appelle une allocation dynamique de la mØmoire, car elle se fait au cours de l’ØxØcution du programme.
Il y a n objets, numØrotØs de 0 n-1.
on peut Øcrire dans la case du premier objet par l’instruction :
(*p)=1;
ou (ce qui est Øquivalent)
p[0]=1;
On peut de mŒme Øcrire dans la case suivante par :
*(p+1)=2; ou p[1]=2;
etc jusqu’ p[n-1].
A la n de l’utilisation, n’oubliez pas de libØrer l’emplacement mØmoire par l’instruction :
delete [ ] p; // on libere les cases mØmoire
Exemple :
#include<iostream> using namespace std; int main() { int n;
cout <<"entrez nbre d’objets n >=0?"<< flush;
cin >> n;
double *p; //on dØclare le pointeur p
p=new double[n]; // on rØserve n cases mØmoire de la classe double
//--- remplissage des cases memoires for(int i=0; i<n; i++) p[i]=2*i;
//---- affichage for(int i=0; i<n; i++) cout<<" p["<<i<<"]="<<p[i]<<endl;
delete [ ] p; // on libere les cases mØmoire
}
6.2.0.1 Remarques
1. On peut rØserver un nombre xe de cases en mØmoire par l’instruction :
double p[6]; // creation de 6 cases mØmoires de type double, numØrotØes de 0 5. p[1] = 3.14; // on Øcrit dans la case 1.
Il s’agit d’une allocation de mØmoire dite statique (non dynamique) car le nombre de case est xØ la compilation, et ne peut Œtre variable. On peut aussi dØclarer et initialiser en mŒme temps : double p[6] = {1, 3.14, 3., 0.};
2. Si l’on veut ne rØserver qu’une seule case mØmoire au lieu d’un tableau, il su t de faire :
int *p; p=new int; // on rØserve une case mØmoire de classe int.
(*p)=1;
delete p; // pour libØrer la place mØmoire
3. Un tableau de caract?res est aussi appelØ une cha ne de caract?res. Pour ce cas il y a une initialisation spØciale :
char *chaine = toto ;
qui dØclare chaine comme Øtant un pointeur sur caract?re, pointant sur les 4 caract?res toto.
Exercice 6.2.2. pointeurs (*) Ecrire un petit programme qui demande l’utilisateur un entier n compris entre 1 et 5, puis alloue de fa on dynamique un tableau p de taille n de la classe string. Ensuite dans une bloucle on demande l’utilisateur le contenu de chaque case du tableau. A la n, on a che le contenu du tableau. L’execution du programme doit donner cela (par exemple) :
entrez n? 3 entrez p[0]? un entrez p[1]? petit entrez p[2]? programme p[0] =un p[1] =petit p[2] =programme
6.3 Utilisation des pointeurs avec la librairie graphique ROOT
Dans l’exercice 4.1.5 vous avez dessinØ une ellipse qui se dØplace l’aide d’une boucle for . Comment dessiner plusieurs cercles simultanØments? Nous vous proposons deux possibilitØs. La premi?re utilise une liste (classe vector), la deuxi?me possibilitØ utilise des pointeurs.
6.3.1 Utiliser une liste d’ellipses Ecrire le programme :
#include <TEllipse.h>
#include <TCanvas.h>
#include <vector>
using namespace std;
int main()
{
TApplication theApp("App", nullptr, nullptr);
TCanvas c( "c","fenetre",400,400); // on precise la taille en pixels
c.Range(-2,-2,8,2); // coordonnees de la fenetre
//-------------
vector<TEllipse> tab_e(3); // liste de 3 ellipses
for (int i=0; i<3; i++)
{ tab_e[i].SetX1(3*i); // coord x du centre tab_e[i].SetY1(0); //coord y du centre tab_e[i].SetR1(1); // rayon selon x tab_e[i].SetR2(1); // rayon selon y
tab_e[i].SetFillColor(kWhite); tab_e[i].Draw(); // dessin
}
//-------------
c.Update();
();
}
Ainsi l’ellipse numØro i+1 n’e ace pas l’ellipse numØro i.
6.3.1.1 Remarques :
Rappel : dans l’instruction for , l’instruction i++ est Øquivalente i = i+1. De mŒme i-- est Øquivalente i = i-1
Pour enlever l’ellipse numØro 1 de la liste, il faut utiliser la fonction erase() de la classe vector : tab_e.erase(tab_e.begin()+1);
Rajouter cette ligne au bon endroit, dans le programme prØcØdent et observer le rØsultat.
6.3.2 Utiliser un pointeur sur l’ellipse Ecrire le programme :
#include <TApplication.h>
#include <TEllipse.h>
#include <TCanvas.h> #include <vector> using namespace std;
int main()
{
TApplication theApp("App", nullptr, nullptr);
TCanvas c( "c","fenetre",400,400); // on precise la taille en pixels
c.Range(-2,-2,8,2); // coordonnees de la fenetre
//-------------
TEllipse *pe; // crØe un pointeur sur TEllipse
for (int i=0;i<3;i++)
{ pe= new TEllipse(3*i,0,1); // pe pointe sur une ellipse
crØe en position x=3*i, y=0, rayon 1 pe->Draw(); // dessin
}
//-------------
c.Update();
();
}
6.3.2.1 Remarques :
L’avantage de cette derni?re mØthode est que l’on peut crØer un nombre indØ ni d’ellipses. Pour modi er une ellipse dØj existante il faudrait stocker au fur et mesure les adresses pe dans un tableau (ou utiliser une fonction de ROOT qui permet de rØcupØrer les adresses des objets existants).
Exercice 6.3.1. Tableau colorØ (*) Ecrire un programme qui dessine un tableau de 10*8 cercles colorØs au hasard comme cela :
Chapitre 7
CrØation d’une classe
Programmer une classe consiste dØ nir un nouveau type d’objets avec des opØrations prØcises que l’on pourra faire avec. Vous avez utilisØ par exemple la classe des Complexes dØj existante (Øcrite par d’autres informaticiens avant vous). Avec cette classe, on peut dØclarer des variables complexes et e ectuer directement des opØrations sur les nombres complexes comme z3=z1*z2*exp(z4), sans devoir dØcomposer l’opØration sur les parties rØelles et imaginaires.
Dans cette section, on va apprendre Øcrire soi-mŒme une classe, et l’on prendra l’exemple des vecteurs et des matrices. Il existe dØj des classes de vecteurs et de matrices prŒtes l’emploi et tr?s performantes, comme armadillo, voir Section 3.3. ConsidØrez donc cette Section comme Øtant but pØdagogique.
7.1 Exemple (Øtape1)
Un vecteur de R3 est dØ nit par ses trois composantes (x,y,z). Voici un programme de dØpart pour dØ nir un vecteur en C++. Essayez ce code. Lisez ensuite les commentaires.
Code :
#include <iostream> using namespace std;
#include <math.h>
// ===============dØclaration de la classe =============== class Vect { public:
double x,y,z;
};
//====== Programme principal =======================
69
int main()
{
Vect v; // declaration v.x=1; v.y=2; v.z=3;
cout << "composante v.y = " << v.y << endl;
}
RØsultat :
composante v.y = 2
7.1.1 Commentaires
L’annonce public: en haut des dØclarations permet ce qui suit d’Œtre connu et accessible hors de la dØclaration de la classe. Au contraire on rend les dØclarations non accessibles (si besoin est) par l’annonce private:
Remarquez dans le programme la prØsence ou non de points virgule; en particulier la n de la dØclaration de la classe.
Exercice 7.1.1. En modi ant le programme prØcØdent, crØer une classe appelØe Musicien qui contient les variables membres : string nom; string instrument; int age; string style; . Dans le programme principal on crØe un objet de cette classe : Musicien robert_J; on initialise les variables et on a che la variable instrument.
7.2 Exemple (Øtape2)
On compl?te le programme prØcØdent. Essayez ce code. Lisez ensuite les commentaires.
Code :
#include <iostream> using namespace std; #include <math.h>
// ===============dØclaration de la classe =============== class Vect
{
public:
//---- variables membres double x,y,z;
//-- le constructeur--Vect()
{ x=0; y=0; z=0;
cout << "vecteur construit " << endl; }
//-- le destructeur--~Vect()
{
cout << "vecteur dØtruit . " << endl; }
//--la fonction Norme--double Norme()
{ double n=0; n=x*x+y*y+z*z; return sqrt(n);
}
};
//====== Programme principal ======================= int main()
{
Vect v; // declaration v.x=1; v.y=2; v.z=3;
cout << "Norme = " << v.Norme() << endl;
}
RØsultat :
vecteur construit Norme = 3.74166 vecteur dØtruit .
7.2.1 Commentaires
Ainsi la deuxi?me ligne du programme principal, le code v.x=1; met la valeur 1 dans la variable membre x de l’objet v. De fa on gØnØrale objet.variable est la fa on de dØsigner une variable associØe un objet.
A la troisi?me ligne du programme principal, le code v.Norme() app?le la fonction Norme() qui est dØclarØe dans la classe. Cela a pour e et de calculer et a? cher
px2 + y2 + z2 = 1 + 4 + 9 = 3.74.. De fa on gØnØrale objet.fonction() est la fa on d’appeler une fonction membre associØe un objet. a la n du programme principal, l’objet v va Œtre dØtruit et le programme appelle la fonction ~Vect() appelØe destructeur, mŒme si cela n’est pas explicitement Øcrit. Dans notre exemple cela a pour e et d’a cher le texte "vecteur dØtruit . ".
Dans la dØclaration d’une fonction membre comme Norme() les variables membre x,y,z s’acc?dent directement (sans la syntaxe v.x) car ce sont sans ambiguitØ les variables membre de l’objet en cours pour qui la fonction a ØtØ appelØe, ici v.
La fonction membre constructeur Vect() est appelØe chaque fois qu’un objet Vect est dØclarØ au cours du programme. Comme son nom l’indique, le constructeur sert initialiser le nouvel objet crØØ. Le constructeur doit toujours porter le nom de la classe en question (ici Vect). Cette fonction particuli?re ne renvoit rien, pourtant on n’Øcrit pas void Vect(). la fonction membre destructeur ?Vect() est appelØe chaque fois qu’un objet Vect est dØtruit au cours du programme. Le destructeur doit toujours porter le nom de la classe en question (ici Vect) avec ~ devant . Cette fonction particuli?re ne renvoit rien, pourtant on n’Øcrit pas void ~Vect().
Exercice 7.2.1. Classes (*)
1. Ecrire le programme ci dessus dans un chier (dans un nouveau rØpertoire /classes/), ØxØcutez le, et vØri ez le comportement attendu.
Vect: x=1 | y=2 | z=3
Appelez cette fonction dans le programme principal et testez le bon fonctionnement.
3. Rajouter une fonction membre de la classe Vect que l’on appelera double Produit(Vect v2), et qui permet de calculer le produit scalaire entre deux vecteurs. Le rØsultat est un double. Dans le programme principal, l’appel doit se faire sous la forme :
Vect v,w;
double d=v.Produit(w);
Aide : dans la fonction double Produit(Vect v2), on Øcrira return x*v2.x+y*v2.y+z*v2.z;
4. Appelez cette fonction dans le programme principal et testez le bon fonctionnement. Remarque : essayez de comprendre la cause des messages construit et dØtruit. Il manque un construit. La raison de cela sera expliquØe ultØrieurement au paragraphe Constructeurs par recopie, et a ectations.
5. Rajoutez un constructeur qui permet d’initialiser directement les variables membres x,y,z du vecteur. Il aura la syntaxe Vect(double xi,double yi,double zi). Dans le programme principal, on pourra alors dØclarer un objet par Vect v(1,2,3); Remarque : vous gardez le constructeur prØcØdent Vect() qui ne prend pas d’argument. N’oubliez pas en e et qu’en C++ des fonctions di Ørentes peuvent avoir le mŒme nom et ne di Ørent que par leurs arguments.
7.3 Utilisation de pointeurs sur objets
Nous avons expliquØ les pointeurs dans la section 6. Dans l’exemple 7.2 ci-dessus, remplacer les quelques lignes du programme principal par
//====== Programme principal ======================= int main()
{
Vect *w = new Vect(); // declaration
w->x=0; w->y=3; w->z=4;
cout << "Norme de w = " << w->Norme() << endl;
}
RØsultat :
vecteur construit
Norme de w = 5
Commentaires
La dØclaration Vect *w signi e que w est un pointeur (c’est dire une adresse) sur un objet de la classe Vect. le code w = new Vect(); a pour e et de crØer un objet cette adresse.
Remarque 7.3.1.
Il n’y a pas d’appel automatique de la fonction destructeur la n du programme. En fait seule la variable pointeur est dØtruite. Si l’on souhaite appeler le destructeur de l’objet pointØ il faut le faire explicitement par w->~Vect(); ou delete(w); qui a le mŒme e et.
Les chiers
Jusqu’ prØsent, nous avons lu des donnØes au clavier et nous les avons a chØ l’Øcran. On peut aussi Øcrire des donnØes dans un chier (sur le disque dur de l’ordinateur ou sur un disque dur ou clef USB externe). Pour cela il faut utiliser des fonctions qui manipulent les chiers. Ces fonctions sont regroupØes dans le chier fstream et font partie des classes ofstream pour Øcrire dans un chier ou ifstream pour lire dans un chier.
Traduction : Output (=sortie) File (= chier) Stream (= ux de donnØes), ou Input(=entrØe),etc..
Reference :
8.1 Ecriture de donnØes dans un chier
reference : ofstream.
Par exemple pour Øcrire un chi re dans un nouveau chier que l’on appellera , il su t de faire 3 Øtapes :
#include <iostream> using namespace std;
#include <fstream> // utilisation des fichiers
int main()
{ ofstream f(""); // ouvre le fichier pour y
ecrire. On lui associe l’objet: f f<<3.1514<<endl; // permet d’ecrire dans le fichier. f.close(); // fermeture du fichier f
}
8.1.0.1 Remarque :
1. On a choisit de terminer le nom du chier par le su xe .txt. Ce n’est pas une obligation, mais une convention : txt signi e texte, et prØcise que ce chier
74
peut se lire comme un texte (mŒme si il y a des chi res). De la mŒme fa on le chier qui contient votre programme se termine par .cc pour signi er que c’est le texte d’un programme C++.
3. L’ouverture du chier se fait par l’initialisation de l’objet f avec le nom du chier donnØ sous la forme d’une chaine de caract?res. On peut donc passer un objet intermØdiaire (chaine de caract?res) de la fa on suivante :
string nom= ofstream f(nom);
4. Lorsque l’on a e ectuØ l’opØration f.close(), f est un objet de la classe ofstream et close() est une fonction de cette classe. On parle de fonction membre. Remarquez la syntaxe : l’objet et la fonction membre sont reliØs par un point.
5. Attention, la commande ofstream f(""); ouvre le chier mais e ace les donnØes existantes si il y en avait. Pour rajouter des donnØes un chier existant (ou non existant) il faut l’ouvrir avec l’instruction ofstream f("",ios::app); ( app signi e append , ajouter )
8.1.0.2 Exercice
Ecrire le programme prØcØdent et vØri er l’existence du chier. Ouvrir le chier dans codeblocks ou avec un autre Øditeur et vØri er son contenu.
8.2 Lecture de donnØes depuis un chier
reference : ifstream.
Le programme suivant permet de lire le chi re Øcrit prØcØdement dans le chier .
#include<iostream> using namespace std;
#include<fstream> // utilisation des fichiers
int main()
{ ifstream g("");// ouvre un nouveau fichier en
lecture. On lui associe l’objet: g double x;
g>>x; //on lit ce qu’il y a au dØbut du fichier, et on
le copie dans l’objet x cout<<x<<endl; // on Øcrit l’Øcran le contenu de x
76
g.close(); // fermeture du fichier g }
8.2.0.1 Remarques :
1. Si le dØbut du chier n’avait pas contenu de chi re (mais des lettres ou rien du tout) le programme n’aurait rien mit dans l’objet x.
3. La lecture g>>x depuis le chier est similaire la syntaxe de lecture cin>>x depuis le clavier.
4. A la place de cout<<x<<endl; on peut Øcrire les instructions :
if(g.good()) cout<<x<<endl;
En e et g.good() renvoit true si la lecture prØcØdente s’est bien faite.
Exercice 8.2.1. (*) Reprendre et modi er le programme de l’exercice 4.6.1 pour que les numØros proposØs par l’utilisateur soient ecrit dans un chier.
8.3 SupplØments sur les chiers
Voici quelques exemples qui montrent comment lire des lignes, des caract?res dans un chier.
#include <iostream> using namespace std; #include <string> #include <fstream> int main()
{
// ecrit des lignes de texte dans un fichier
ofstream f(""); f<<"ligne1: abc"<<endl; f<<"ligne2: def"<<endl; f.close();
// ouvre le fichier en lecture ifstream g("");
string l; while(g.good()) {
getline(g, l);// -> l. copie la ligne dans l (sans le code retour
a la ligne) cout<<l<<endl; // affiche ligne l
}
g.clear(); // remet les indicateurs zero pour continuer la recherche.
// .
g.seekg(0, g.beg); // se place au debut du fichier (begining + 0) int p;
p=g.tellg(); // -> position p dans le fichier
cout<<"p="<<p<<endl;
char c;
for(int i=0; i<=5; i++)
{
g.get(c); // -> c. Lit caractere
cout<<c;
} cout<<endl; p=g.tellg(); cout<<"p="<<p<<endl; }
rØsultat
ligne1: abc ligne2: def p=0 ligne1 p=6
Remarque 8.3.1. g.seekg(0, g.beg); est Øquivalent g.seekg(g.beg); Comme autre option il y a g.seekg(0, g.end); (position n de chier + 0) ou g.seekg(0, g.cur); (position actuelle + 0)
Chapitre 9
Micro-projets 1
Choisissez et rØalisez un de ces micro-projet, selon votre motivation et l’aisance que vous ressentez en informatique.
Pour le graphisme, utiliser la Section 16.
Nous demandons de produire un compte rendu sous forme pdf et html, que l’on peut rØdiger par exemple avec Voir Section 14.6.
La Section suivante donne quelques recommandations gØnØrales et prØlables pour e ectuer un projet de programmation.
9.1 Que faire avant de programmer?
Avant de se retrouver devant un ordinateur pour Øcrire un programme, il faut avoir Øtabli clairement ce qu’on dØsire lui faire faire. Voici l’ensemble des Øtapes suivre, indispensables, faire au prØalable devant une feuille de papier.
1. DØ nir clairement l’objectif du programme (surtout s’il y a plusieurs personnes y collaborer).
2. DØ nir l’ensemble des t ches qui doivent Œtre accomplies dans un ordre logique pour atteindre cet objectif. Pour un projet scienti que il faut que toutes les formules mathØmatiques soient bien Øcrites.
3. Tracer sur un papier un organigramme illustrant le fonctionnement de votre programme. VØri ez que l’ordre d’exØcution des diverses t ches va e ectivement faire ce que vous attendez.
En fait, votre organigramme se traduira directement dans la partie principale de votre programme (fonction int main()). A chaque t che correspondra l’appel d’une sous partie (une fonction).
78
4. DØterminez la mØthode (algorithme) permettant de remplir chaque t che. Il est possible qu’une sous-t che soit commune plusieurs fonctions. Dans ce cas, faites-en une nouvelle fonction. Cependant, pour Øviter une multiplication de ces fonctions, ne le faites que pour celles faisant plusieurs lignes de programme. L’art de la programmation consiste trouver un compromis entre la simplicitØ, la longueur, et la rapiditØ d’ØxØcution de votre programme.
9.1.1 Quelques notions de qualitØ en informatique
La qualitØ d’un programme ne se traduit pas simplement en termes d’e cacitØ mais Øgalement en termes de sØcuritØ ( abilitØ) et d’exportabilitØ. Autrement dit, vous devez Øcrire votre programme de telle sorte (1) que n’importe qui puisse le lire et comprendre ce qu’il fait et (2) qu’il puisse Œtre aisØment testØ. Voici quelques indications :
modulaire : chaque fonction devrait Œtre indØpendante en vue d’Œtre Øventuellement utilisØe dans un autre programme. Dans ce but n’hØsitez pas crØer une nouvelle classe d?s que le besoin se fait sentir.
utilisable par autrui : chaque fonction (et classe) devrait avoir en entŒte des commentaires indiquant,
1. son objectif,
2. l’algorithme ou mØthode utilisØe,
3. la liste des objets d’entrØe,
4. la liste des objets de sortie,
5. (Øventuellement) des instructions sur la mani?re de l’appeler et un exemple d’utilisation.
Cet ensemble d’informations correspond une sorte de cahier des charges d’une fonction. Lors d’un travail en Øquipe, il appartient chacun de travailler sur des fonctions di Ørentes. Il faut donc se mettre d’accord au prØalable sur la mani?re dont chaque fonction va dialoguer avec les autres. lisible : tant que possible, utiliser des noms Øvocateurs pour les objets (mais pas trop longs) et les fonctions.
able : Øviter au maximum l’utilisation d’objets globaux, sauf si ils permettent un vØritable gain en lisibilitØ (passage d’arguments moins frØquents). Dans ce cas, indiquer dans un commentaire le (ou les) objet(s) global(aux) qu’une fonction attend.
9.2 Suite de Syracuse
Di cultØ : facile.
Soit un nombre entier x0 ? 1. Par rØcurrence, on dØ nit xt+1 partir de xt par :
si xt est pair si xt est impair
La valeur de dØpart x0 = 7 donne la suite 7,11,17,26,13,20,10,5,16,8,4,2,1,2,1,2,1,
La fameuse conjecture de Syracuse non dØmontrØe ce jour est que partant de toute valeur x0 la suite arrive forcØment au bout d’un moment T (qui dØpend de x0) la suite pØriodique 1,2,1,2,
Exercice 9.2.1. Ecrire un programme qui demande x0 l’utilisateur et a che la suite (xt)t jusqu’ ce que la valeur xt = 1 soit atteinte.
Solution 9.2.2. Faire :
1. Ecrire une fonction S (x) qui prend un entier x en entrØe et renvoit est pair ou sinon.
2. Dans le programme principal, on demande x l’utilisateur. Tant que x 6= 1, faire
(a) x = S (x)
(b) A che x
Exercice 9.2.3. Pour x0 donnØ, on note T (x0) la plus petite valeur de t ? N telle que xt = 1. Tracer T en fonction de x0 ? {1,2, 1000}.
Solution 9.2.4. Voici l’algorithme. Faire une fonction T = L(x) qui prend un entier x en entrØe et renvoit T: 1. On pose T = 1.
2. Tant que x 6= 1 faire :
(a) on calcule x = S (x).
(b) T = T + 1
Ensuite,
1. CrØer une fenetre graphique, avec des axes x0 = 1 ? 1000 et T = 0 ? 100,
2. on parcourt x0 = 1 ? 1000,
(a) on calcule T = L(x0)
(b) on dessine un point en (x0,T)
9.3 Pendule simple
Di cultØ : moyenne.
Dessiner (en animation) dans l’espace rØel et dans l’espace des phases, le mouvement d’oscillation d’un pendule simple de masse m, longeur l, xØes, soumis la force de pesanteur P = mg avec g = 9.8m/s2 et une force de frottement de coef ?.
Equations physiques : si a(t) est la position angulaire et la vitesse angulaire au temps t on a
oø Fa (a,b),Fb (a,b) sont des fonctions de a,b. (vØri er ces formules en appliquant la loi de Newton sur la droite verte tangente au cercle.).
Position du pendule :
x = lsin(a), y = ?lcos(a)
a(t + dt) = a(t) + dt · Fa
b(t + dt) = b(t) + dt · Fb
En e et ces formules dØcoulent de , et de mŒme pour b.
Voici comment on utilise ces formules. On choisit un pas de temps dt tr?s petit. Par exemple dt = 0.01. On discrØtise le temps avec un pas de temps dt : la date de dØpart est t0. ensuite il y a t1 = t0 + dt, t2 = t1 + dt, etc..
On suppose que a0 = a(t0),b0 = b(t0) sont les conditions initiales connues. Avec les formules de Euler on calcule a(t1),b(t1), puis a(t2),b(t2)
On fait le dessin du pendule au fur et mesure du calcule.
Structure du programme Ecrire une fonction :
/* Formule du champ de vecteur sur le plan (a,b) entree: a,b sortie: Fa,Fb
*/
void F(double a, double b, double & Fa,double & Fb)
{
// code ici }
Dans la fonction main, partir de valeurs a,b initiales, calculer a,b chaque pas de temps et dessiner le pendule.
9.4 La fractale du dragon
Di cultØ : moyenne.
Si on prend une feuille de papier que l’on plie N = 1 fois et que l’on impose au pli de faire un angle droit (90 ) on obtient la forme suivante. Si on plie N = 2 fois ou N = 3 fois ou N = 16 fois on obtient les formes suivantes assez surprenantes. La forme pour s’appelle la fractale du dragon.
Ecrire un programme qui demande une valeur N ? 1 et dessine la forme obtenue si l’on plie N fois. Qu’observez vous de surprenant?
Exercice 9.4.1. On peut coder la forme N xØ, comme une suite SN de 2N ? 1 valeurs ±1, oø +1 (respect. ?1) signi e que le pli est droite (respect. gauche). Par exemple S1 = {1}, S2 = {?1,1,1}, S3 = {?1,?1,1,1,?1,1,1}. Montrer que la suite SN+1 se dØduit facilement de la suite SN.
Solution 9.4.2. Si la suite SN (i) est connue alors
SN = SN?1 ? {1} ? SN?1
oø ?SN?1 est la suite SN?1 Øcrite l’envers avec l’opposØ des ØlØments.
Solution 9.4.4. N ? 1 est donnØ. On part de la liste S = {1}. On pose n = 1. Tant que n < N faire :
1. On copie S0 = S. On pose d = S.size().
2. On rajoute 1 au dØbut de la liste S0.
3. Pour i = 0 ? d ? 1, on rajoute ?S [i] au dØbut de la liste S0.
4. On copie S = S0. On fait n = n + 1.
Exercice 9.4.5. Ensuite, pour la partie graphique, il su t de lire cette liste et dessiner des segments la suite. Ecrire l’algorithme.
Solution 9.4.6. On code une direction par un chi re : dir=0,1,2,3 pour respectivement :
droite, haut, gauche, bas.
1. On initialise (x1,y1) = (0,0) et dir = 0 et i = 0.
2. On calcule (x2,y2) partir de (x1,y1) de la fa on suivante :
(a) Si dir = 0 alors (x2,y2) = (x1 + 1,y1)
(b) Si dir = 1 alors (x2,y2) = (x1,y1 + 1)
(c) etc
3. On change de direction : dir = (dir + SN (i) + 4)%4. (Remarque : on a Øcrit +4 car il y a un bug sur le modulo en langage C/C++.)
4. On dessine une droite de (x1,y1) (x2,y2).
5. On pose (x1,y1) = (x2,y2) et i = i + 1.
6. Si i < SN.size() on retourne en (2).
9.5 Images subliminales
Dans le code de la conscience de Stanilas Dehaene, p.66, il est expliquØ qu’une image de durØe <0.33 sec et entourØe d’autres images ne sera pas percue conscienment. E ectuer ce type d’expØriences avec des formes gØomØtriques.
9.6 Equation de la chaleur
Di cultØ : moyenne.
Notons x = (x1, xn) les variables d’espace et t la variable de temps. Joseph Fourier a dØcouvert en 1822 que la propagation de la tempØrature T (x,t) dans un matØriau est rØgit par l’Øquation d’Øvolution
appelØe Øquation de la chaleur, avec l’opØrateur Laplacien :
On rempla ant les dØrivØes par des di Ørences nies montrer que l’Øquation de la chaleur ci-dessus devient :
.
Partant d’un champ de tempØratures quelconque, en utilisant cette modØlisation, Øcrire un programme qui fait Øvoluer et dessine le champ de tempØrature. Discuter le choix des param?tres ,h. Pour les matrices, on utilisera la classe mat de armadillo.
9.7 Le jeu puissance 4
niveau facile (di cile si option de stratØgie).
Faire un programme qui permet de jouer deux personnes au jeu de puissance 4, et a che le rØsultat sur le terminal (avec cout). Stocker l’Øtat du jeu dans une matrice d’entiers.
En option, on peut jouer contre l’ordinateur. DØvelopper une stratØgie par recherche arborescente.
9.8 Le jeu de la vie
Di cultØ : moyenne.
En utilisant une matrice d’entiers de taille 10*10 avec des conditions pØriodiques, faire Øvoluer et dessiner les coe cients de matrice selon les r?gles simples du jeu de la vie.
9.9 Algorithme de Monte-Carlo pour le mod?le d’Ising
Di cultØ : moyenne di cile.
Figure 9.1 RØsultat de l’algorithme de Monte-Carlo pour le mod?le d’aimantation de
Ising, aux tempØratures ? = 1/(kbT) = 0.3 (dØsordre), 0.4 (transition de phase) et 0.5 (ordre magnØtique).
(Explication physique : voir Cours de Master 1: syst?me dynamiques, chapitre Dynamique probabiliste sur les graphes ).
On consid?re un rØseau N × N pØriodique dont les sites sont notØs X = (x,y) et dont les variables sont fX = ±1 et modØlisent des spins (up/down). Si deux sites sont voisins (chaque site a 4 voisins) on note X ? Y . Une con guration des spins est un champ donnØ :
f = (fX)X. Son Ønergie ferromagnØtique est :
E (f) := X X (?fX.fY )
X Y,X?Y
Les con gurations d’Ønergie minimale sont celles donnant (?fX.fY ) minimal soit fX.fY = 1 pour tous X,Y . Il y a donc deux con gurations d’Ønergie minimale qui sont : fup={fX = 1 pour tous site X} et fdown={fX = ?1 pour tous site X}.
Les con gurations d’Ønergie minimale sont celles donnant (?fX.fY ) maximal soit fX.fY = ?1 pour tous X,Y . Cela est possible si Nest pair avec des spins dont les valeurs sont alternØes (il y a deux con gurations).
9.9.1 Algorithme de Monte-Carlo
L’algorithme d’Øvolution suivant est appelØ algorithme de montecarlo. ? ? 0 est un param?tre xØ qui est l’inverse de la tempØrature : ? = 1/(kbT).
1. A l’instant n = 0, on part d’une con guration f choisie au hasard.
2. On choisit un site X0 au hasard, et on note f0 la con guration identique f sauf au site X0 oø le spin est opposØ : fX00 = ?fX0 (spin opposØ).
3. Choix de la con guration l’instant n + 1,
(a) Si E (f0) < E (f) on choisit la con guration f0.
(b) Si E (f0) ? E (f) on choisit la con guration f0 avec la probabilitØ Pf?f0 = exp(??.(E (f0) ? E (f))) (et on choisit f avec la probabilitØ complØmentaire).
4. On revient en (2) pour poursuivre l’Øvolution du champ de spins.
Remarque 9.9.2. Cet algorithme correspond une matrice stochastique rØversible pour la mesure de Boltzmann :
Exercice 9.9.3.
1. Montrer que la condition E (f0) < E (f) s’Øcrit simplement en fonction du site X et de ses voisins.
2. Ecrire un programme qui fait Øvoluer et reprØsente la con guration du champ de spins fX selon l’algorithme de Monte-Carlo. (Utiliser une matrice d’entiers avec armadillo et le dessin de TH2D expliquØ en Section 16.3.1 avec l’option col).
9.10 Zeros d’un polynome alØatoire
On consid?re un polynome P (x) de degrØ N ? 1 et coe cients Pk alØatoires et indØpendants, selon une loi uniforme Pk ? [?1,1] :
On sait que P a N zØros (zi)i=0?N dans le plan complexe. Dessiner les N zØros du polynome et observez. Quelle conjecture avez vous? Consulter cet article de Terence Tao et al.. On peut aussi considØrer des coe cients complexes alØatoires : Pk = Rk + iIk avec
Rk,Ik ? [?1,1] alØatoires et indØpendants.
Aide : les zØros du polynome sont les valeurs propres de la du polynome. Construire cette matrice et utiliser la fonction eigen() de armadillo pour trouver les valeurs propres.
sls sls d
Deuxi?me partie
ComplØments sur le langage C++
89
Chapitre 10
Autres environnements
10.1 Programmer avec l’environnement codeblocks
Au lieu d’utiliser emacs comme ci-dessus vous pouvez utiliser un environnement de programmation plus convivial ou ØvoluØ comme codeblocks. Le dØsavantage est qu’il nØcessite un certain apprentissage, qu’il e ectue automatique certaines taches votre place et vous perdez un peu le contr le de votre programmation.
Remarque 10.1.1. Il existe aussi d’autres environnements de dØveloppement sophistiquØs comme eclipse ou emacs.
Exercice 10.1.2.
1. Lancer le programme codeblocks.
2. Choisir "create a new project
3. Faire les choix : projet de type "Console application , C++; lui donner un nom, comme "projet1 et choisir le rØpertoire oø il sera hØbergØ.
Dans le rØpertoire Sources , il appara t un chier appelØ contenant le code C++ suivant :
#include <iostream> using namespace std; int int main()
{ cout << "Hello world!" << endl;
return 0;
}
4. Une fois pour toute on va choisir la version 11 du C++. Pour cela, dans le menu Settings/Compiler/Compiler Flags, cocher l’option de compilation -std=c++11
90
CHAPITRE 10. AUTRES ENVIRONNEMENTS 91
6. Pour compiler et exØcuter ce programme, taper F9 (ou build and run dans le menu). Il apparait alors une fenŒtre (console) avec le message "Hello world ! .
Dans la suite, vous pouvez modi er le code de ce programme pour l’adapter aux exemples donnØs ci-dessous. Vous pouvez aussi crØer d’autres projets de la mŒme mani?re.
Pour copier un exemple, vous pouvez "copier le code donnØ en exemple dans ce tutoriel et le "coller dans votre Øditeur, sans le retaper.
Chapitre 11
Ecrire et lire des donnØes en binaires sur un chier (ou un ux)
11.1 Bits, octets, bytes, taille d’un objet
Or dans la mØmoire de l’ordinateur une objet numØrique (ex : int i=17 ) est stockØe en binaire, i.e. en base 2, sous forme d’une suite de 0 et 1 appelØs des bits. Une sØquence de 8 bits est appellØe un octet.
Le est un ensemble de bits qui dØ nit l’unitØ de stockage. En gØnØral un byte contient 8 bits, mais cela peut varier selon l’ordinateur. La variable syst?me CHAR_BIT contient le nombre de bits contenus dans un byte. En gØnØral CHAR_BIT=8.
La commande sizeof(type) indique le nombre byte occupØ par un type donnØ. Le programme :
#include<iostream> using namespace std; #include <limits.h> int main() {
cout<<"sizeof(int)="<<sizeof(int)<<endl; cout<<"CHAR_BIT="<<CHAR_BIT<<endl; }
a che :
sizeof(int)=4 CHAR_BIT=8
Par exemple l’objet int i=17 est stockØ en mØmoire en base 2 par une suite de 4*8=32 bits soit 4 octets :
92
00010001 00000000 00000000 00000000 Voici le taille des principales types de base :
Classe | char | short int | int | long | float | double | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Taille (en byte) | 1 | 2 | 4 | 4 | 4 | 8 #include<iostream> using namespace std; #include <bitset> int main() { int i=17; cout<<"La variable int i="<<i<<" est de taille "<<sizeof(i)<<" bytes."<<endl; unsigned char *p=(unsigned char*)&i; // Le pointeur p est l’adresse memoire de i cout<<"Contenu des bytes, en ecriture base 10:"<<endl; for(int i=0; i<sizeof(i);i++) cout<<" "<<(int)p[i]; cout<<endl<<"Contenu des bytes, en ecriture base 2:"<<endl; for(int i=0; i<sizeof(i);i++) cout<<" "<<bitset<8>(p[i]); cout<<endl; } A che : La variable int i=17 est de taille 4 bytes. Contenu des bytes, en ecriture base 10: 17 0 0 0 Contenu des bytes, en ecriture base 2: 00010001 00000000 00000000 00000000 11.2 Fichiers en format texte Dans les le ons prØcØdentes, nous avons vu comment Øcrire des donnØes numØriques dans un chier. Ces donnØes Øtaient Øcrites sous forme de chi res (ex : 17) et sont lisibles depuis un Øditeur. On dit qu’il s’agit d’une Øcriture en format texte ou format ASCII; car un objet numØrique est reprØsentØ en base dix par un ou plusieurs chi res dans le chier. Lorsque l’on Øcrit dans un chier en format texte (par la commande fichier<<i<<endl; ) l’ordinateur convertit le nombre en base dix et Øcrit la suite des chi res, ce qui donne 17. (Il faut savoir que chaque caract?re est codØ par un octet en mØmoire, selon le codage ASCII). L’avantage du format texte est que l’on peut visualiser le contenu du chier l’aide d’un Øditeur comme emacs. Rappels sur le code ASCII : cout <<(int)’A’ << endl; // --affiche 65 qui est le code ASCII de A cout <<(char)(65) << endl; //--affiche A qui a pour code ASCII: 65 11.3 Fichiers en format binaire 11.3.0.1 Exemple Le programme suivant montre comment Øcrire et lire en format binaire, et comment se positionner dans un chier. #include <stdlib> #include <iostream> using namespace std; #include <fstream> #include <iomanip> int main() { double x=3.14; int i=17; int nx=sizeof(double); //nombre de byte occupØs par un objet de classe double int ni=sizeof(int); //nombre de byte occupØs par un objet de classe int cout<<"nx= "<<nx<<" ni= "<<ni<<endl; //=============================== ofstream sortie( " ",ofstream::binary); //ouverture d un fichier en ecriture -sortie.write(&x, nx); //--- ecriture de x --sortie.write(&i, ni); //--- ecriture de i --sortie.close(); //- fermeture du fichier -- //=============================== ifstream entree( " ", ifstream::binary); //-- ouverture d un fichier en lecture (&x, nx); //--- lecture de x (&i, ni); //--- lecture de i --cout<<" x= "<<x<<" i= "<<i<<endl; entree.seekg(nx); // on se positionne apr?s nx octets en partant du dØbut, soit apr?s la valeur de x (&i, ni); //--- lecture de i --cout<<" i= "<<i<<endl; int pos=entree.tellg(); // donne la position du curseur dans le fichier (en byte) cout<<"la position du curseur est maintenant: "<<pos<<endl; } 11.3.0.2 Remarques Pour se positionner en lecture, il faut utiliser la fonction seekg. Pour se positionner en Øcriture, il faut utiliser la fonction seekp. Le premier argument de ces fonctions indique le dØplacement relatif du pointeur (en byte). Le deuxi?me argument (facultatif) indique l’origine du dØplacement. Ce peut-Œtre : le dØbut du chier : ios::beg la position actuelle du curseur : ios::cur la n du chier ios::end exemple : entree.seekg(nx,ios::beg); // on se positionne apr?s nx octets en partant du dØbut Quelques commandes utiles 12.0.1 La commande system La commande system() permet de lancer d’autres commandes ou programmes sur l’ordinateur. Par exemple pour lancer la commande ls qui a che la liste des chiers : #include <iostream> // pour cout using namespace std; #include <stdlib.h> // pour system //----Fonction principale ---------------int main() { system("ls"); // execute la commande ls. (affiche les fichiers) } 96 Chapitre 13 Classes (suite) Cette Section fait suite la Section 7. 13.1 SurdØ nitions d’opØrateurs 13.1.1 Exemple d’opØrateur binaire Pour e ectuer le produit scalaire entre deux vecteurs, plut t que d’utiliser la fonction Produit ci dessus, on aimerait utiliser la syntaxe plus agrØable : d=v*w; cela est possible en redØ nissant l’opØrateur * de la fa on suivante. Code rajouter dans le code des fonctions membres : double operator*(Vect v2) { double p=0; p=x*v2.x+y*v2.y+z*v2.z; return p; } Utilisation dans le programme principal : int main() { double d; Vect v(1,2,3); Vect w(2,3,4); d=v*w; cout << d= << d << endl; } 97 13.1.2 Commentaire La syntaxe de l’opØration * est semblable celle de la fonction membre Produit. Comme pour Produit, Il est important de remarquer Vect v2 est un param?tre de l’opØration *. Lorsque l’on Øcrit d=v*w, l’opØration * est appelØe pour l’objet v qui se trouve gauche de * (et non w); l’appel de cette opØration, le vecteur v2 prend la valeur de w qui se trouve droite de *. On dit que * est un opØrateur binaire car lors de l’appel d=v*w il y a deux entrØes pour *. Il y a sa gauche l’objet de la classe, et sa droite un autre objet ( priori quelconque, ici Vect v2). (),[ ], -> , *,/,%,+,-, << ,>>,<,<=,>,>=,==,!=,&,^, ||,&&,|,=, +=,-=,*=,/=,%=,&=,^=,|=, <<=,>>=,et la virgule ,. Exercice 13.1.1. (*) 1. Ecrivez le code pour e ectuer la somme (termes termes) et la di Ørence de deux vecteurs par la syntaxe : w=u+v; w=u-v; et testez. 2. Ecrivez le code pour e ectuer le produit et la division externe d’un rØel a avec un vecteur v (produit de chaque ØlØment) par la syntaxe w=v*a; ou w=v/a; et testez. 3. On aimerait accØder aux variables membres v.x,v.y,v.z respectivement, par la syntaxe v[1],v[2],v[3]. Cela est possible en dØ nissant l’opØrateur [ ] avec la dØclaration double & operator [ ](int i); A l’utilisation, on Øcrit par exemple d=v[1];. Ecrivez le code de cet opØrateur et testez. Rem : le signe & permet d’Øcrire aussi une a ectation comme v[1]=3;. Cela sera mieux expliquØ au paragraphe a ectations . 13.1.3 Exemple d’un opØrateur unaire Comme en mathØmatiques, on aimerait donner un sens (?v) qui est l’opposØ du vecteur v. Pour cela il faut Øcrire : Code rajouter dans le code des fonctions membres : Vect operator-() { return (*this)*(-1); } Utilisation dans le programme principal : int main() { Vect v(1,2,3); Vect w; w=-v; w.Affiche(); } 13.1.4 Commentaires On dit que - est un opØrateur unaire car lors de l’appel w=-v car la seule entrØ de - est l’objet v de la classe qui se trouve droite. (il n’y a pas d’entrØe gauche). Les symb les possibles pour un opØrateur unaire sont : +,-,++,--,!,~,*,&,new,new[],delete,(cast) (*this) dans la fonction membre signi e l’objet en cours. Ici il s’agit du vecteur v. Remarquez la fa on Øconomique de calculer le rØsultat : on utilise la multiplication externe dØj programmØe. Exercice 13.1.2. Simpli ez le code de l’opØrateur binaire w=v/a dØj Øcrit en utilisant l’opØrateur binaire * . 13.2 Fonctions amies 13.2.1 Exemple La syntaxe que permet les fonctions membres ci-dessus a une petite limitation. Dans la classe Vect, on aimerait par exemple dØ nir le produit externe * d’un double a avec un Vect v, et Øcrire : w=a*v; Mais d’apr?s la disposition des objets, il faudrait que * soit un opØrateur binaire de l’objet a qui se trouve gauche . On peut cependant associer cette opØration la classe Vect de l’objet v en Øcrivant : Code rajouter dans le code des fonctions membres : friend Vect operator*(double a,Vect v2) { return (v2*a); } Utilisation dans le programme principal : int main() { double d(2); Vect v(1,2,3); Vect w; w=d*v; } 13.2.2 Commentaire Le prØ xe friend signi e que cet opØrateur n’est pas exactement une fonction membre de la classe Vect, mais pas non plus indØpendante puisque elle est dØclarØe avec la classe. Remarquez l’ommission du prØ xe de rattachement Vect:: . Les param?tres de l’opØration * dans la dØclaration sont (double a,Vect v2).Ils se trouvent de part et d’autre de * lors de l’utilisation : w=a*v; Comme en mathØmatiques, on a dØ nit a*v=v*a. Mais on pourrait Øcrire ce que l’on veut et ne pas respecter la commutativitØ. la dØclaration double d(2) est Øquivalente double d=2. (La signi cation est que le constructeur double(int) est appelØ.) 13.2.3 Exemple de cout << Code rajouter dans le code des fonctions membres : friend ostream & operator <<(ostream & sortie, Vect v2) { sortie <<"Vecteur: "<<v2.x <<"|"<<v2.y <<"|"<<v2.z <<"|"; return sortie; } Utilisation dans le programme principal : int main() { Vect v(1,2,3); cout <<v <<endl; } Exercice 13.2.1. (*) Ecrivez le code nØcessaire a n de pouvoir demander le contenu d’un vecteur l’utilisateur par la syntaxe cin >>v; L’utilisateur doit entrer les composantes la suite des questions Vect x=?,y=?,z=?. La syntaxe est cette fois-ci : istream & operator >>(istream & entree,Vect & v2); Le symbole & s’appelle passage par rØfØrences, et est nØcessaire puisque le vecteur v est modi Ø la sortie de la fonction. 13.3 Vecteurs de taille variable On va amØliorer la classe de vecteurs de R3 prØcØdente, en permettant d’avoir des vecteurs de RN avec N quelconque, au choix de l’utilisateur. Important : avant de commencer, nous conseillons vivement de (re)lire le paragraphe sur les pointeurs. Il appara t que les variables membres de la nouvelle classe, doivent Œtre : (Code Øcrire dans la dØclaration de la classe :) //---- variables membres int N; //taille double *element; //pointeur Important : la valeur de N nous renseigne tout moment sur la taille de l’espace mØmoire rØservØ. Cette modi cation nous obligera rØØcrire les fonctions membres de la classe Vect, mais aussi d’introduire deux nouvelles fonctions membres : le constructeur par recopie, et l’opØrateur d’a ectation =. Exercice 13.3.1. CrØer une nouvelle classe de vecteurs, qui contient les variables membres ci-dessus, avec les fonctions membres suivantes : Une fonction membre Libere() qui lib?re l’espace mØmoire de taille N. (1) Lib?re l’emplacement mØmoire dØj existante (si N>0) (2) :RØserve l’emplacement mØmoire de taille Ni , (si Ni>0). (3) :met N=Ni. (4) met les composantes du vecteur zØro. Un constructeur Vect(), qui met seulement N = 0, et ne rØserve pas de place mØmoire. (il appelle la fonction : Init(0); ) Un constructeur Vect(int Ni), pour crØer un vecteur de taille Ni (il appelle la fonction : Init(Ni); ). Un destructeur ~Vect() qui lib?re l’espace mØmoire. (il appelle la fonction Libere()) 13.3.1 Constructeur par recopie Dans un exercice prØcØdent, nous avons Øcrit une fonction membre double Produit (Vect v2). Lors de l’appel de cette fonction par la syntaxe v.Produit(w) dans le programme principal, un objet v2 est crØØ, et l’objet w est copiØ dedans. Mais il Øtait apparu qu’aucun Constructeur n’Øtait explicitement appelØ pour cette recopie. La raison est que le compilateur C++ a appelØ un constructeur de recopie par dØfaut, qu’il a crØØ lui-mŒme. Dans ce constructeur par dØfaut, il est e ectuØe une recopie simple des variables membres (il s’agissait de x,y,z). Dans le cas actuel, une recopie simple des variables membres ne su t pas. A la crØation du nouvel objet Vect v2, il faut en e et rØserver l’emplacement mØmoire, et donc Øcrire nous mŒme le constructeur par recopie. Le code est le suivant : Code rajouter dans le code des fonctions membres : Vect (const Vect & v2) { // code Øcrire (exercice) } 13.3.2 OpØrateur d’a ectation = Dans la classe Vect de R3, si v,w sont deux vecteurs, et que l’on Øcrit l’opØration d’a ectation : v=w; On va Øcrire nous mŒme le code de l’opØrateur d’a ectation = Code rajouter dans le code des fonctions membres : Vect & operator=(const Vect & v2) { // code Øcrire (exercice) } Exercice 13.3.2. 1. Ecrivez le code du constructeur par recopie et de l’opØrateur d’a ectation = ci dessus, et testez. (en a chant des messages). 2. Reprenez les fonctions membres de la classe Vect de R3 prØcØdente, et Øcrivez les versions Vect de RN correspondantes. Concernant spØcialement l’opØrateur [ ], avec N ØlØments, on dØcide d’y accØder de la fa on suivante : Vect v(N); v[0],v[1], ,v[N-1], Rem : Dans le code de l’opØrateur [ ](int i), il est bien de tester si l’indice i est dans l’intervalle autorisØ, et sinon d’a cher un message d’erreur (i.e. si i<0, ou i>N-1). 3. Dans un deuxi?me temps, on aimerait que le premier indice i1 soit quelconque (et pas forcØment i1 = 0). Par exemple, que les trois composantes d’un vecteur de R3soit v[2],v[3],v[4],i.e. i1 = 2,N = 3; PrØcisement, on voudrait qu’un vecteur soit caractØrisØ par sa taille N, et par son premier indice i1, a n d’accØder aux ØlØments du vecteur par la commande v[i] avec. Il faut donc rajouter i1comme variable membre de la classe, et modi er les constructeurs et destructeurs, et fonctions membres si besoin est. A la n, on aura une classe de vecteur assez sophistiquØe, et prŒte l’emploi. 4. (Long) Sur le mŒme mod?le que la classe vecteur, crØer une classe matrice, avec les opØrations usuelles. Rem : on pourra dØ nir une matrice N*M comme Øtant un tableau de vecteurs, et prendre comme variables membres: int N,M; // taille Vect * ligne; Rem : attention aux fonctions Init() et Libere() ! Chapitre 14 Gestion de projets avec chiers .h, Make le, Git et Doxygen 14.1 Projet avec plusieurs chiers, chier .h Voici un projet informatique en C++, tr?s simple (et sans trop de sens) qui est fait de 3 chiers : un chier qui contient la fonction main() par laquelle commence le programme. un chier qui contient une classe Vect et une fonction f utilisØes dans la fonction main. un chier main.h qui contient les dØclarations de la classe Vect et de la fonction f. Le su xe .h signi e header (comme entŒtes ). 14.1.0.1 RØsultat du programme : Norme de v=1 3*3=9 14.1.0.2 Code c++ //== f i c h i e r main . cc ============= #include <iostream> using namespace std ; #include "main . h" int main () 104 { Vect v ; v . x=1; cout<<"Norme de v="<<v .Norme()<<endl ; cout<<"3*3="<<f(3)<<endl ; } Remarque 14.1.1. Au dØbut du chier , l’instruction #include "main.h" a pour e et d’inclure le contenu du chier main.h cet endroit. Il y a les dØclarations (mais pas le code) de la classe Vect et de la fonction f qui sont utilisØes dans la fonction main(). //== f i c h i e r autres . cc ============= #include "main . h" #include <math .h> //??? contenu du constructeur Vect : : Vect () { x=0; y=0; z=0; } //?????contenu de la fonction Norme double Vect : : Norme() { return sqrt (x*x+y*y+z*z ); } //??? code de la fonction f double f ( double x) { return x*x ; } Remarque 14.1.2. Le chier contient le code des fonctions membres de la classe Vect et le code de la fonction f(). Noter la syntaxe particuli?re Vect : :Norme() qui signi e fonction Norme de la classe Vect. //== f i c h i e r main . h ============= #i f ! defined (__MAIN_H) #define __MAIN_H //?????? d ’ une classe class Vect { public : double x , y , z ; double Norme ( ) ; }; // ????? declaration d ’ une fonction double f ( double x ); #endif Remarque 14.1.3. Le chier main.h contient les dØclarations (et non pas le code) de la classe Vect et de la fonction f(). Les lignes particuli?res : #if!de ned(__MAIN_H) #de ne __MAIN_H #endif ne sont pas nØcessaires ici mais sont souvent utiles : ce sont des instructions du prØprocesseur c’est dire que au moment de la compilation, si le compilateur voit le code intermØdiaire la premi?re fois, il le consid?re (et met la variable __MAIN_H 1). Les fois suivantes, il ne consid?re par cette partie de code. L’intØrŒt de cela est que si le chier main.h se trouve rØpØtØ cause d’inclusions multiples (par exemple le chier A.cc inclut main.h et B.h, et le chier B.h inclut main.h), alors le code n’est Øcrit qu’une seule fois, ce qui Øvite une erreur de code dØj dØclarØ . 14.1.0.3 Commandes de compilation g++ -c -o main.o g++ -c -o autres.o g++ main.o autres.o -o main Remarque 14.1.4. Le premi?re ligne signi e on compile le chier c++ et on fabrique un chier en langage machine main.o . De mŒme pour la deuxi?mre ligne. La troisi?me ligne signi e partir des hciers main.o et autres.o on fabrique le chier exØcutable nal main . 14.2 Un programme Make le pour gØrer la compilation Dans l’exemple de la section 14.1.0.3, si les chiers sont importants et si on e ectue une modi cation du code que dans le chier , on souhaiterait de pas refaire la commande de compilation g++ -c -o autres.o. Le programme Makefile permet de gØrer cela automatiquement (il regarde les dates de modi cation des chiers). Voici un tutoriel sur le Make le. Voici le # ??? f i c h i e r Makefile main . o : main . cc main . h g++ ?c main . cc ?o main . o autres . o : autres . cc main . h g++ ?c autres . cc ?o autres . o f i n a l : main . o autres . o g++ main . o autres . o ?o main Commande de compilation on Øcrit : make final rØsultat : g++ -c -o main.o g++ -c -o autres.o g++ main.o autres.o -o main Commentaires : Dans le chier Make le, les deux lignes main.o: main.h g++ -c -o main.o signi ent que si on souhaite main.o , cela dØpend des chiers et main.h, et si un de ces derniers a ØtØ modi Ø il faut ØxØcuter la commande g++ -c -o main.o la commande make nal signi e que l’on souhaite final. Cela a pour e et d’enclencher les commandes de compilation nØcessaires. Essayer par exemple de modi er seulement le chier et refaites make final. Le rØsultat est : g++ -c -o main.o g++ main.o autres.o -o main 14.2.1 Un programme Make le plus pratique Le programme suivant est totalement Øquivalent au prØcØdent mais il est plus pratique car on a placØ des instructions de compilation dans des variables (en lettres majuscules). Ainsi si vous rajoutez des librairies, il su t de la faire un seul endroit. De plus gr ce au symbols [email protected] et $* on Øvite des rØØcritures. # ??? f i c h i e r Makefile # . . . . l i b r a i r i e s LIBR = ?lm # . . . instructions de compilation CC =g++ ?std=c++11 OBJETS= main . o autres . o main . o : main . cc main . h $(CC) ?c $ *. cc ?o [email protected] autres . o : autres . cc main . h $(CC) ?c $ *. cc ?o [email protected] f i n a l : main . o autres . o $(CC) $(OBJETS) ?o main $(LIBR) lib : ar r main clean : rm *. o Remarque 14.2.2. Par exemple dans ce tutorial, on utilise souvent beaucoup de librairies et au total on Øcrira : LIBR=$(shell root-config --libs) $(shell root-config --glibs) -lm -lpthread CC =g++ -std=c++11 $(shell root-config --cflags) # compilateur On a rajoutØ l’entrØe clean qui permet d’e acer tous les chiers en langage machine .o par la commande make clean On a rajoutØ l’entrØe lib pour crØer une librairie. 14.2.2 Generation automatique du chier make le? Voir generation avec GNU 14.3 Gestion de versions d’un projet avec Git Le logiciel permet de gØrer di Ørentes versions d’un projet. Il se souvient de l’historique. On lui indique une liste de chiers ( le dØpot ) et rØguli?rement on lui demande de faire une sauvegarde (par l’instruction commit) 14.3.0.1 Utilisation de Git sur son ordinateur ]La premiere fois : Dans le rØpertoire de travail, taper git init Cela initialise un rØpertoire .git/ Ecrire (en mettant votre nom et adresse) : git config --global "Frederic Faure" git config --global user.email Cela modi e le chier ~/.gitconfig A faire au cours du projet : git status :montre l’Øtat des chiers par rapport au dØpot. git add ou git add ’*.cc’ ou git add * pour ajouter un (des) chier(s) au dØpot git commit -m " rst commit" fait une sauvegarde des chiers du dØpot avec le titre indiquØ git log pour voir l’historique des opØrations git rm chier :enleve un chier du dØpot git mv le1 le2 : deplace ou change le chier. git di pour lire les di erences Dans le chier .gitignore il y a les extensions et repertoires ignorer. 14.4 Collaboration et partage de projets sur internet avec GitHub Si vous dØveloppez un projet avec des collaborateurs, vous pouvez dØposer (gratuitement) votre projet sur le site (par exemple). Au dØpart il faut vous crØer un compte sur github qui va hØberger votre projet. Si votre projet s’appelle projet, et votre compte s’appelle fdrfaure Commandes : git remote add projet Cela associe votre projet local au dØpot sur le site web git push -u projet master Cela dØpose les derni?res modi cations de votre projet sur le site web git pull expanding_map master Cela permet de rØcupØrer les derni?res modi cations (posØes par les collaborateurs) qui sont sur le site web. git clone repertoire_local Cela copie le dØpot du site web sur votre rØpertoire local. (a faire une premiere fois pour copier un projet) 14.5 Commenter et documenter un projet avec Doxygen Nous expliquons l’utilisation de base de pour documenter automatiquement un projet en c++. 14.5.1 CrØation et consultation de la documentation 14.5.1.1 CrØation de la documentation html Se placer dans le rØpertoire du projet. Dans une console, lancer le programme doxywizard et con gurer les param?tres. On gØn?re la documentation par Run . Cela crØe des rØpertoires : html et latex. 14.5.1.2 consultation de la documentation On lit le chier Pour la documentation pdf, dans le rØpertoire latex, executer la commande make. Cela gØn?re le chier 14.5.2 conventions pour Øcrire les commentaires 14.5.2.1 Page principale : (voir mainpage) Ecrire : /*! \mainpage Titre \section Nom_section texte */ CHAPITRE 14. GESTION DE PROJETS AVEC FICHIERS .H, MAKEFILE, GIT ET DOXYGEN111 14.5.2.2 Le rØsultat suivant : est obtenu avec le code suivant (syntaxe markdown) : Header 1 {#labelid} ======== ## Header 2 ## {#labelid2} [Link text](#labelid) [Link text](@ref labelid) - Item 1 More text for this item. - Item 2 + nested list item. + another nested item. - Item 3 1. First item. 2. Second item. __double underscores__ The link text]() [The link text]( "Link title") [The link text]( "Link title") [The link text]() [The link text](@ref MyClass) ![Caption text]()  ![Caption text][img def] ![img def] [img def]: "Optional Title" > <> <> <> <> First Header | Second Header ------------- | ------------- Content Cell | Content Cell Content Cell | Content Cell | Right | Center | Left | | ----: | :----: | :---- | | 10 | 10 | 10 | | 1000 | 1000 | 1000 | This is a paragraph introducing: ~~~~~~~~~~~~~~~~~~~~~ a one-line code block ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~{.c} int func(int a,int b) { return a*b; } ~~~~~~~~~~~~~~~ ‘ also a fenced code block ‘ Mathematical Inline Formula: \f$\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}\f$. or \f[ |I_2|=\left| \int_{0}^T \psi(t) \left\{ u(a,t)- \int_{\gamma(t)}^a \frac{d\theta}{k(\theta,t)} \int_{a}^\theta c(\xi)u_t(\xi,t)\,d\xi \right\} dt \right| \f] or \f{eqnarray*}{ g &=& \frac{Gm_2}{r^2} \\ &=& \frac{(6.673 \times 10^{-11}\,\mbox{m}^3\,\mbox{kg}^{-1}\, \mbox{s}^{-2})(5.9736 \times 10^{24}\,\mbox{kg})}{(6371.01\,\mbox{km})^2} \\ &=& 9.82066032\,\mbox{m/s}^2 \f} 14.5.3 Fichier de commentaires avec la syntaxe markdown On peut Øcrire le chier du projet avec la syntaxe markdown . Voici une rØfØrence sur Markdown. avec 14.6 Compte rendu de projet en pdf et html avec Lyx Lyx est un logiciel qui produit des documents scienti ques en di Ørents formats (pdf, html, etc). La documentation est bien faite. Voici quelques informations prØcises. On peut exporter en html : Exporter en xhtml avec Lyxhtml. Insertion de videos sous format gif animØ. Pour des prØsentation on peut choisir le style Beamer Chapitre 15 Interface interactive pour le C++ Il y a le projet cling. Voir Cling. Il y a le projet qui permet d’e ectuer du C++ scienti que de fa on interactive sur le cloud . 116 Troisi?me partie ComplØments sur les librairies du langage C++ 117 118 Dans les chapitres suivants nous prØsentons quelques librairies C++ utiles. Nous conseillons de consulter la page de Boost qui prØsente d’autres librairies. Voici en particulier une liste de librairies pour l’audio, le graphisme. Chapitre 16 Librairie Root : graphisme et autre.. (complØments) Nous avons dØj vu une introduction cette librairie en Section 3.4. Cette Section apporte quelques complØments. Voici aussi la Documentation gØnØrale. 16.1 Graphisme de base 2Dim 16.1.1 Objets graphiques Voici quelques exemples de Remarquez que au dØbut du programme on ajoute une ligne spØci que par exemple #include <TLine.h> si on utilise la classe TLine. #include <TApplication.h> #include <TCanvas.h> #include <TLine.h> #include <TEllipse.h> 119 #include <TMarker.h> #include <TBox.h> #include <TText.h> int main() { TApplication theApp("App", nullptr, nullptr); //.. La fenetre TCanvas *c=new TCanvas("titre","titre",400,400); c->Range(0,0,1,1); //.. une ligne bleue TLine *l=new TLine(0.1,0.5,0.3,0.9); // x1,y1,x2,y2. l->SetLineColor(kBlue); // bleu l->Draw(); //.. Une Ellipse (ou cercle) TEllipse *e= new TEllipse(0.2,0.3,0.1); // (centre (x,y) et rayon r e->Draw(); //.. Un point TMarker *m=new TMarker(0.3,0.4,8); // (x,y) et 6 ou 8: forme du Marker m->SetMarkerColor(kPink); m->Draw(); //.. Un rectangle vert TBox *p=new TBox(0.15,0.6,0.25,0.7); // on precise les coins bas-gauche (x1,y1) et haut droit (x2,y2) : p->SetFillColor(kGreen); p->Draw(); //.. Du texte c->Update(); // Montre le dessin (); } 16.1.2 Dessiner dans plusieurs fenŒtres Avec la classe TCanvas qui hØrite de TPad. #include <TApplication.h> #include <TCanvas.h> #include <TMarker.h> int main() { TApplication theApp("App", nullptr, nullptr); TCanvas c1("c1","fenetre1",400,400); // creation de la fenetre c1 TCanvas c2( "c2","fenetre2",400,400); // creation de la fenetre c2 TMarker *m1=new TMarker(0.3,0.4,8); // un point TMarker *m2=new TMarker(0.3,0.4,8); // un point m2->SetMarkerColor(kRed); (); // choix de la fenetre c1 pour le prochain dessin m1->Draw(); // dessin du point 1 (); // choix de la fenetre c2 pour le prochain dessin m2->Draw(); // dessin du point 2 (); } 16.1.3 Un cadre avec des axes graduØs : DrawFrame Avec la classe TPad. #include <TApplication.h> #include <TCanvas.h> int main() { TApplication theApp("App", nullptr, nullptr); TCanvas *c=new TCanvas("titre","titre",400,400); double x1(0),y1(0),x2(2),y2(1); // position du cadre c->DrawFrame(x1,y1,x2,y2); (); } On peut complØter le dessin du cadre de la fa on suivante. On rØcup?re un pointeur sur un histogramme de la classe TH1, et on a acc?s aux fonctions membres pour modi er le cadre, par exemple : #include <TApplication.h> #include <TCanvas.h> #include <TEllipse.h> #include <TH1F.h> int main() { TApplication theApp("App", nullptr, nullptr); TCanvas *c=new TCanvas("titre","titre",400,400); double x1(0),y1(0),x2(10),y2(10); // position du cadre TH1F *h =c->DrawFrame(x1,y1,x2,y2); TEllipse e(2,3,1); e.Draw(); h->SetXTitle("x"); h->SetYTitle("y"); h->SetMinimum(-2); // change les bornes en y h->SetMaximum(8); h->SetBins(1000,1,5); // change les bornes en x: 1->5 c->Update(); // Montre le dessin (); } 16.1.4 Un seul axe graduØ : TGaxis #include <TApplication.h> #include <TCanvas.h> #include <TGaxis.h> int main() { TApplication theApp("App", nullptr, nullptr); TCanvas *c=new TCanvas("titre","titre",400,400); double x1(0.1),y1(0.5),x2(0.9),y2(0.5); // position de l’axe double v1(0),v2(1); // graduations extremes TGaxis axe(x1,y1,x2,y2,v1,v2,510,""); axe.SetTitle("x"); // met le titre sur l’axe (); (); } 16.1.5 Couleurs voir #include <TApplication.h> #include <TCanvas.h> #include <TStyle.h> #include <TColor.h> #include <TH3.h> //==================== main () { TApplication theApp("App", nullptr, nullptr); //---- on defini une palette de 5 couleurs const int n = 5; //nbre de couleurs float rgb[n * 3] = {0.9f, 0.60f, 0.f, 0.f, 1.f, 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f}; int palette[n] = {0}; for (Int_t i = 0; i < n; ++i) palette[i] = TColor::GetColor(rgb[i * 3], rgb[i * 3 + 1], rgb[i * 3 + 2]); gStyle->SetPalette(n, palette); //------------------------ gStyle->SetCanvasPreferGL(kTRUE); // permet d’utiliser openGL TCanvas *c = new TCanvas("glc","TH3 Drawing", 400,400); int N=50; double x1=-3,x2=3,y1=-3,y2=3,z1=-3,z2=3; TH3D *h = new TH3D("h", "h", N, x1, x2, N, y1, y2, N, z1, z2); for(int i=0;i<N;i++) for(int j=0;j<N;j++) for(int k=0;k<N;k++) { double x= (x2-x1)*i*1./N+x1; double y= (y2-y1)*j*1./N+y1; double z= (z2-z1)*k*1./N+z1; double f= exp(-x*x-y*y-z*z); // formule f(x,y,z) h->SetBinContent(i+1,j+1,k+1, f); } h->SetFillColor(kBlue); h->SetContour(n); h->Draw("gliso");// glbox1 glbox glcol gliso (); } 16.2 Graphisme 1D Voici quelques exemples d’utilisation des Pour utiliser ces exemples, il faut remplacer les parties (C) de l’exemple de la Section 3.4.2, par le code ci-dessous. Attention : la ligne #include <..> mentionnØe chaque fois est mettre en haut de votre programme. 16.2.1 Courbe a partir de l’expression d’une formule y = f (x) : TF1 Avec la classe TF1. 16.2.1.1 Exemple partir d’une formule littØrale : #include <TApplication.h> #include <TF1.h> int main() { TApplication theApp("App", nullptr, nullptr); //------------ TF1 *f1 = new TF1("f1","sin(x)/x",0,10); f1->Draw(); (); } 16.2.1.2 MŒme rØsultat partir d’une fonction C++ #include <TApplication.h> #include <TF1.h> //--------------------double F(double *px, double *p) { double x= px[0]; return sin(x)/x; } //--------------int main() { TApplication theApp("App", nullptr, nullptr); //------------ TF1 *f1 = new TF1("f1",F,0,10); f1->Draw(); (); } 16.2.2 Histograme 1Dim ou Courbe y (x) partir de tableau de valeurs y (i) : TH1D Avec la classe TH1D. #include <TApplication.h> #include <TH1.h> int main() { TApplication theApp("App", nullptr, nullptr); //------------ int nx(10); // nombre de points double xmin(0.0),xmax(1.0); // graduation de l’axe x TH1D *h1=new TH1D("nom","titre",nx,xmin,xmax); h1->SetXTitle("x"); h1->SetYTitle("y"); for(int i=1;i<=nx;i++) // remplissage { double z=i*i; h1->SetBinContent(i,z); } h1->SetStats(0);// pas de boite de resultats stat h1->SetMarkerStyle(8);//8: gros point h1->Draw("L P"); // option L: ligne ou P: marker (); } 16.2.2.1 ReprØsentation en diagramme Camembert (Pie Chart) Voir TPie. MŒme exemple ci-dessus avec l’option h1->Draw("PIE 3d"); et h1->Draw("PIE rsc"); 16.2.3 ReprØsentation d’un tableau de valeurs y (i) sous forme de camembert ou Pie Voir TPie. #include <TApplication.h> #include <TPie.h> int main() { TApplication theApp("App", nullptr, nullptr); //------------ int couleurs[5] = {2,3,4,5,6}; TPie *p = new TPie("p", "titre", n, valeurs, couleurs); p->Draw(); (); } 16.2.4 Courbe paramØtrØe (x(i),y (i)) avec axes partir de tableaux de valeurs x(i),y (i) : TGraph Attention : si les valeurs x(i) son ØquidistribuØes il est prØfØrable d’utiliser la classe TH1 ci-dessous. La classe TGraph est prØfØrables si les valeurs x(i) et y (j) ne sont pas ØquidistribuØes. Avec la classe TGraph. Voir Options de dessin. Exemple : #include <TApplication.h> #include <TGraph.h> #include <TPad.h> #include <TAxis.h> int main() { TApplication theApp("App", nullptr, nullptr); //---- creation de tableaux int n = 100; double x[100], y[100]; for (int i=0;i<n;i++) // remplissage { double r = i; x[i] = r*cos(0.3* i); y[i] = r*sin(0.3 * i); } //--- dessin TGraph *gr = new TGraph(n,x,y); gr->SetLineColor(kBlue); gr->SetMarkerColor(kRed); gr->SetMarkerStyle(6); // 6:petit cercle 8: gros gr->GetXaxis()->SetTitle("X"); gr->GetYaxis()->SetTitle("Y"); gr->Draw("A C P"); // AC: trace un segment entre les points // gPad->BuildLegend(); // rajoute la legende (); } 16.2.5 Courbe paramØtrØe (r (i),? (i)) reprØsentØe en coordonnØes polaire partir de points r (i),? (i) Voir Options de dessin. #include <TApplication.h> #include <TCanvas.h> #include <TGraphPolar.h> int main() { TApplication theApp("App", nullptr, nullptr); TCanvas * c = new TCanvas("CPol","TGraphPolar Example",500,500); int N=50; double theta[N]; double radius[N]; for (int i=0; i<N; i++) { theta[i] = i*(2*M_PI/N); radius[i] = sin(theta[i])* sin(theta[i]) +1; } TGraphPolar * grP1 = new TGraphPolar(N, theta, radius); grP1->SetFillColor(kRed); grP1->Draw("f"); c->Update(); (); } Autre exemple : #include <TApplication.h> #include <TCanvas.h> #include <TGraphPolar.h> int main() { TApplication theApp("App", nullptr, nullptr); TCanvas * c = new TCanvas("CPol","TGraphPolar Example",500,500); double theta[8]; double radius[8]; double etheta[8]; double eradius[8]; for (int i=0; i<8; i++) { theta[i] = (i+1)*(M_PI/4.); radius[i] = (i+1)*0.05; etheta[i] = M_PI/8.; eradius[i] = 0.05; } TGraphPolar * grP1 = new TGraphPolar(8, theta, radius, etheta, eradius); grP1->SetTitle("TGraphPolar Example"); grP1->SetMarkerStyle(20); grP1->SetMarkerSize(2.); grP1->SetMarkerColor(kBlue); grP1->SetLineColor(kRed); grP1->SetLineWidth(3); grP1->Draw("PE"); c->Update(); (); } 16.2.6 CrØe histogramme ou fonction 1D y = f (x) par tirage alØatoire et ajustement par une formule : TF1 Remarque 16.2.1. voir Code : #include <TRandom.h> #include <TApplication.h> #include <TCanvas.h> #include <TF1.h> #include <TH1.h> #include <iostream> using namespace std; int main() { TApplication theApp("App", nullptr, nullptr); TCanvas *c=new TCanvas("titre","titre",400,400); //------- cree histo avec tirage aleatoire avec loi gaussienne -----------int nbin=100; double xmin=-3,xmax=3; TH1D *h=new TH1D("TW","TW",nbin,xmin,xmax); gRandom->SetSeed(); // initialise sur l’horloge for(int i=1;i<=1e5;i++) //tirages { double x= gRandom->Gaus(0,1); // c’est P(x)=exp(-0.5*(x/sigma)^2) h->Fill(x); } h->Draw(); //---- calcule integrale et normalise l’histogramme ----------double I= h->Integral(); h->Scale(1/I); h->Draw(); //--- Fit l’histogramme avec une Gaussienne -------------- TF1 *f1 = new TF1("f1","gaus"); // ou pol0, pol1, polN pour polynome degre N, ou expo h->Fit("f1"); cout<<"Ecart type ="<<f1->GetParameter(2)<<endl; //--- montre fenetres de commandes --h->DrawPanel(); h->FitPanel(); (); } 16.2.7 Superposition d’histogrammes : THStack Classe THStack. #include <TApplication.h> #include <TH1.h> #include <THStack.h> int main() { TApplication theApp("App", nullptr, nullptr); //---- cree un histogramme -------int nx(10); // nombre de points double xmin(0.0),xmax(1.0); // graduation de l’axe x TH1D *h1=new TH1D("nom","titre",nx,xmin,xmax); h1->SetXTitle("x"); h1->SetYTitle("y"); for(int i=1;i<=nx;i++) // remplissage { double z=i*i; h1->SetBinContent(i,z); } h1->SetFillColor(kGreen); //-------------- une copie ---------TH1D *h2 = (TH1D*)h1->Clone(); h2->SetFillColor(kRed); //------------------------THStack *hs = new THStack("hs",""); hs->Add(h1); hs->Add(h2); hs->Draw(); // dessin les uns sur les autres. nostack: superposes , nostackb, lego1 (); } 16.3 Graphisme 2D 16.3.1 Surface 2Dim : z = f (x,y) partir d’un tableau de valeurs z (i,j) : TH2D Avec la classe TH2D. #include <TApplication.h> #include <TH2D.h> int main() { TApplication theApp("App", nullptr, nullptr); //-----------int nx(10),ny(10); double xmin(0),xmax(1),ymin(0),ymax(2); TH2D *h= new TH2D("h1","titre",nx,xmin,xmax,ny,ymin,ymax); h->SetXTitle("x"); h->SetYTitle("y"); h->SetStats(kFALSE); // pas de panneau stat for(int i=1;i<=nx;i++) // remplissage for(int j=1;j<=ny;j++) { double z=i*j; h->SetCellContent(i,j,z); } h->Draw("surf2"); // essayer options: surf1, surf3,surf4,lego //------------------- (); } 16.3.2 Surface 2D a partir de l’expression d’une formule z = f (x,y) : TF2 Avec la classe TF2. Voici le rØsultat du programme suivant avec di Ørentes options, en particulier, reprØsentation cylindrique, polaire, spherique et en utilisant openGL. #include <TApplication.h> #include <TF2.h> #include <TStyle.h> int main() { //------------ gStyle->SetCanvasPreferGL(kTRUE); //si option gl.. cidessous TF2 *f2 = new TF2("f2","-sin(x)*sin(y)/(x*y)",0,5,0,5); f2->Draw("surf4"); // glsurf1, glsurf2, glsurf3, glsurf3, glsurf1cyl, glsurf1pol, glsurf1sph (); } 16.3.3 Surface 2D z (x,y) partir d’un ensemble arbitraire de points x(i),y (i),z (i) : TGraph2D La classe TGraph2D permet de dessiner une surface z (x,y) partir de la donnØe d’un ensemble de N points (xi,yi,zi). #include <TApplication.h> #include <TGraph2D.h> #include <TCanvas.h> #include <TRandom.h> #include <TStyle.h> using namespace std; main () { TApplication theApp("App", nullptr, nullptr); TCanvas *c = new TCanvas("c","Graph2D example",0,0,600,400); double x, y, z, P = 6.; int N = 200; TGraph2D *g = new TGraph2D(); TRandom *r = new TRandom(); for(int i=0; i<N; i++) { x = 2*P*(r->Rndm(N))-P; // choix de (x,y) au hasard dans le carrØ [-P,P] y = 2*P*(r->Rndm(N))-P; z = (sin(x)/x)*(sin(y)/y)+0.2; // formule de la surface lisse g->SetPoint(i,x,y,z); // ajoute point (x,y,z) a l’ensemble } gStyle->SetPalette(1); // couleurs. autres choix: 0, 1, 2 g->Draw("tri1"); // autres choix: tri, triw, tri1, tri2, p,po, pcol,line (); } 16.3.4 Champ de vecteur en dimension 2 /// Ce code dessine un champ de vecteur spØci Ø deux dimensions. #include <TApplication.h> #include <TCanvas.h> #include <TArrow.h> #include <TLine.h> #include <math.h> #include <iostream> using namespace std; ///--- definition du champ de vecteur void vect(double x, double y, double &vx, double &vy) { vx = x; vy= -y; } //---------------------------------int main() { TApplication theApp("App", nullptr, nullptr); //------------ double xmin=-1, xmax=1, ymin=-1,ymax=1; // coordonnØes TCanvas *c=new TCanvas("c","c",500,500); // fenetre //-----------------double N=20; // nbre d’echantillons double dx=(xmax-xmin)/N; double dy=(ymax-ymin)/N; for (int i=0; i<N; i++) for (int j=0; j<N; j++) { double x=xmin + dx*i; double y=ymin + dy*j; double vx,vy; vect(x,y,vx,vy);// calcul les coordonnØes du champ de vecteur vx *= dx *1; // normalise vy *= dy *1; double n=sqrt(vx*vx+vy*vy); // norme TArrow *t= new TArrow(x,y,x+vx,y+vy,dx*n); // TLine *t= new TLine(x,y,x+vx,y+vy); t->Draw(); } // for i,j // -donne la main a l’utilisateur (A) (); } 16.4 Graphisme 3D 16.4.1 Des points dans R3 : TPolyMarker3D Doc : TPolyMarker3D. Remarque : ensuite avec le menu on peut rØgler leur opacitØ etc #include <TApplication.h> #include <TPolyMarker3D.h> int main() { TApplication theApp("App", nullptr, nullptr); //------------ int N=1000;//nbre de points TPolyMarker3D *p = new TPolyMarker3D(N); for( int i = 0; i < N; i++ ) { double theta=i/double(N)*2*M_PI; double x=cos(theta), y=sin(theta), z=cos(10*theta); p->SetPoint( i, x, y, z ); } p->SetMarkerSize( 1 ); p->SetMarkerColor( kBlue); p->SetMarkerStyle( 8 ); p->Draw(); (); } 16.4.2 Surface dØterminØe par f (x,y,z) = 0 : TF3 Ici la surface dans R3 est dØterminØe par l’Øquation f (x,y,z) = 0 avec f donnØe par son expression. On utilise la classe TF3. the options "FB" and "BB" suppress the "Front Box" and "Back Box" around the plot. #include <TApplication.h> #include <TCanvas.h> #include <TStyle.h> #include <TPaveLabel.h> #include <TFormula.h> #include <TF3.h> // utiliser la mollette de la souris pour zoomer // et touche ’s’ pour changer la couleur main () { TApplication theApp("App", nullptr, nullptr); //--- prepare la fenetre 3D gStyle->SetCanvasPreferGL(1); TCanvas *cnv = new TCanvas("glc", "TF3: Klein bottle", 200, 10, TPad *tf3Pad = new TPad("box", "box", 0.04, 0.04, 0.96, 0.8); tf3Pad->Draw(); //--- definition et dessin de l’objet 3D TFormula f1 = TFormula("f1", "x*x + y*y + z*z + 2*y - 1"); TFormula f2 = TFormula("f2", "x*x + y*y + z*z - 2*y - 1"); TF3 *tf3 = new TF3("Klein Bottle","f1*(f2*f2-8*z*z) + 16*x*z*f2", -3.5, 3.5, -3.5, 3.5, -2.5, 2.5); tf3->SetFillColor(kRed); tf3Pad->cd(); tf3->Draw("gl"); // gl, default, kMaple0, kMaple1, kMaple2 (touche ’s’) (); } 16.4.3 Fonction f (x,y,z) ?R : TH3 Doc : TH3. Dessin avec openGL et en option : dessin de la surface de niveau de f ( gliso ) ou boules montrant la valeur de f ( glbox ) ou densitØ transparente glcol . Remarques : avec l’option surface de niveau gliso la touche c permet de voir des coupes et les dØplacer avec la souris. #include <TApplication.h> #include <TCanvas.h> #include <TStyle.h> #include <TH3.h> main () { TApplication theApp("App", nullptr, nullptr); gStyle->SetCanvasPreferGL(kTRUE); // mettre avant declaration de la fenetre TCanvas TCanvas *c = new TCanvas("glc","TH3 Drawing", 400,400); int N=30; double x1=-3,x2=3,y1=-3,y2=3,z1=-3,z2=3; TH3D *h = new TH3D("h", "h", N, x1, x2, N, y1, y2, N, z1, z2); for(int i=0;i<N;i++) for(int j=0;j<N;j++) for(int k=0;k<N;k++) { double x= (x2-x1)*i*1./N+x1; double y= (y2-y1)*j*1./N+y1; double z= (z2-z1)*k*1./N+z1; double f= exp(-x*x-y*y-z*z); // formule f(x,y,z) h->SetBinContent(i+1,j+1,k+1, f); } h->SetFillColor(kBlue); double levels[2] = {2, 2.5}; // valeurs des niveaux h->SetContour(2,levels); // on choisit deux niveaux pour le dessin iso h->Draw("gliso");// glbox1 glbox glcol gliso (); } 16.4.3.1 Dessin d’une surface de niveau souhaitØe Sans l’option gl , mais seulement iso , la surface de niveau dessinØe est pour la valeur Voici un exemple qui dessine la surface px2 + y2 + z2 = 2 c’est dire la sph?re de rayon 2 : #include <TApplication.h> #include <TCanvas.h> #include <TStyle.h> #include <TH3.h> main () { TApplication theApp("App", nullptr, nullptr); //------------------double C0=2; // niveau souhaite TCanvas *c = new TCanvas("glc","TH3 Drawing", 400,400); int N=30; double x1=-3,x2=3,y1=-3,y2=3,z1=-3,z2=3; TH3D *h = new TH3D("h", "h", N, x1, x2, N, y1, y2, N, z1, z2); double sum=0; for(int i=0;i<N;i++) for(int j=0;j<N;j++) for(int k=0;k<N;k++) { double x= (x2-x1)*i*1./N+x1; double y= (y2-y1)*j*1./N+y1; double z= (z2-z1)*k*1./N+z1; double f= sqrt(x*x+y*y+z*z); // fonction h->SetBinContent(i+1,j+1,k+1, f); sum+=f; } double delta = -sum + (N*N*N) *C0; h->SetBinContent(1,1,1, h->GetBinContent(1,1,1)+delta); h->SetFillColor(kBlue); h->Draw("iso"); (); } 16.4.3.2 Dessin d’une courbe sur une surface On peut combiner le dessin d’une iso-surface de la Section 16.4.3.1 avec le dessin d’une courbe de la Section 16.4.1 (mais sans l’option GL malheuresement). Voici un exemple : un grand cercle Øquateur sur la sph?re de rayon 2. Malheuresement, la partie arri?re de la courbe n’est pas cachØe. #include <TApplication.h> #include <TCanvas.h> #include <TStyle.h> #include <TH3.h> #include <TPolyMarker3D.h> main () { TApplication theApp("App", nullptr, nullptr); //------------------double C0=2; // niveau souhaite TCanvas *c = new TCanvas("glc","TH3 Drawing", 400,400); int N=30; double x1=-3,x2=3,y1=-3,y2=3,z1=-3,z2=3; TH3D *h = new TH3D("h", "h", N, x1, x2, N, y1, y2, N, z1, z2); double sum=0; for(int i=0;i<N;i++) for(int j=0;j<N;j++) for(int k=0;k<N;k++) { } double delta = -sum + (N*N*N) *C0; h->SetBinContent(1,1,1, h->GetBinContent(1,1,1)+delta); h->SetFillColor(kBlue); h->Draw("iso"); //------------ int N2=1000;//nbre de points TPolyMarker3D *p = new TPolyMarker3D(N2); for( int i = 0; i < N2; i++ ) { double theta=i/double(N2)*2*M_PI; double x=2*cos(theta), y=2*sin(theta), z=0; p->SetPoint( i, x, y, z ); } p->SetMarkerSize( 1 ); p->SetMarkerColor( kRed); p->SetMarkerStyle( 8 ); p->Draw(); (); } 16.5 Utilisation de la souris Dans une fenetre gaphique, chaque objet capture la souris si elle passe proximitØ. Pour l’observer, vous pouvez activer l’option EventStatus dans le menu de la fenetre. Voici un code complet qui de nit une classe que l’on appelle Fenetre (hØritØe de la classe TCanvas). La nouveautØ est la fonction HandleInput() qui est appelØe chaque fois que la souris bouge devant la fenetre. Lorsque le programme execute l’instruction (); il est en attente d’Øv?nements provenant du clavier ou de la souris. Si un tel Øv?nement se produit, il appelle la fonction void HandleInput(EEventType event, Int_t px, Int_t py), en passant les param?tres de l’Øv?nement. C’est donc dans cette fonction que vous allez rajouter du code (ou faire appel d’autres fonctions). #include <iostream> using namespace std; #include <TCanvas.h> #include <TMarker.h> #include <TApplication.h> #include <TEllipse.h> //====Ma Classe Fenetre================================ class Fenetre: public TCanvas { public: // constructeur Fenetre(Text_t* name, Text_t* title, Int_t ww, Int_t wh) : TCanvas(name,title,ww { } // - Cette fonction est appelle si la souris s’agitte. void HandleInput(EEventType event, Int_t px, Int_t py) { // affiche l’etat de la souris // convertit la position en pixel px,py en coordonnees (x,y) double x = gPad->AbsPixeltoX(px); double y = gPad->AbsPixeltoY(py); // si bouton gauche appuye dessine un point if(event==1) { TMarker *m=new TMarker(x,y,8); m->Draw(); Update(); } // Event clavier if (event==kKeyPress) // touche appuyØe { cout<<"touche px="<<px<<endl; switch (px) { // change de mode (tempØrØ <-> juste) case ’m’: cout<<"Touche m appuyØe"<<endl; break; }; // switch px } // if event } }; ///-------------------------------------------int main() { TApplication theApp("App", nullptr, nullptr); Fenetre *c = new Fenetre("c", "fenetre1", 400,400); // crØe une fenetre double xmin(0),ymin(0),xmax(5),ymax(5); c->Range(xmin,ymin,xmax,ymax); // coordonnees de la fenetre (optionnel, par defaut: 0-1) (); } 16.5.1 Pour qu’un objet graphique capture les evenements souris Dans l’exemple suivant une ellipse change d’Øtat selon les evenement souris (clique gauche). Pour cela il faut crØer une classe dØrivØe qui poss?de la fonction ExecuteEvent(int event, int px, int py). #include <iostream> using namespace std; #include <TCanvas.h> #include <TApplication.h> #include <TEllipse.h> //============== class Cercle: public TEllipse { public: // constructeur Cercle(double x, double y, double r): TEllipse(x,y,r) { } int col = kRed; // - Cette fonction est appelle si la souris s’agitte. void ExecuteEvent(int event, int px, int py) { //--- change l’epaisseur du cercle SetLineWidth(3); Draw(); gPad->Update(); SetLineWidth(1); Draw(); //--- si clique gauche if(event == 1) { if(col == kRed) col = kBlue; else col = kRed; Draw(); } } }; ///-------------------------------------------int main() { TApplication theApp("App", nullptr, nullptr); TCanvas *c = new TCanvas("c", "fenetre1", 400,400); // crØe une fenetre double xmin(0),ymin(0),xmax(4),ymax(4); c->Range(xmin, ymin, xmax, ymax); // coordonnees de la fenetre (optionnel, par defaut: 0-1) Cercle *C1= new Cercle(3,2,0.5); C1->SetFillColor(kRed); C1->Draw(); Cercle *C2= new Cercle(1,2,0.5); C2->SetFillColor(kRed); C2->Draw(); (); } 16.5.2 Pour gØrer la souris lorsque le programme calcule Si votre programme est lancØ dans un calcul interminable, et que vous vouliez cependant modi er des param?tres par l’intermØdiaire de la souris ou d’un commande clavier, il faut dans votre boucle mettre la commande suivante : gSystem->ProcessEvents(); // avec #include <TSystem> Ainsi si la souris est actionnØe, dans l’exemple ci-dessus, la fonction Fenetre::ExecuteEvent sera appelØe. 16.6 Comment crØer un chier gif animØ Voici un exemple complet qui permet de crØer un chier animØ partir d’images crØØes par root. Travail prØalable : 1. Fichiers tØlØcharger et sauver dans son rØpertoire : et . 2. Il faut avoir installØ le logiciel , #include <TApplication.h> #include <TCanvas.h> #include <TEllipse.h> #include "" ///-----------------------------------int main() { TApplication theApp("App", nullptr, nullptr); TCanvas *c= new TCanvas("c","fenetre",400,400); double xmin(-1),ymin(-1),xmax(2),ymax(2); c->Range(xmin,ymin,xmax,ymax); int periode = 10; // temps entre chaque image en 1/100e sec. int imin=1, imax=30; for(int i =imin; i<=imax; i++) { double x = i/(double)imax; TEllipse e(x,0.5,1); e.Draw(); // dessine l’ellipse c->Update(); Animation(c,i,imin,imax,"film",periode); // -> crØe fichier } (); } 16.7 Boutons de commandes (Widgets et GUI) Le mot widget est une contraction de windows et gadget . GUI signi e Graphical User Interface . Documentation Exemple tr?s complet qui montre presque toutes les possibilitØs et comment le faire : copier/coller. (attention, il y a une erreur : remplacer (char **) par (const char **)). Voir aussi les nombreux exemples dans /usr/share/doc/root/tutorials/gui que l’on execute avec l’interprØteur et la commande .x nom.C 16.7.1 Exemple simple avec quelques widgets Voici un exemple plus simple, copier/coller, donnant l’interface : /*! =========================== * Exemple simple d ’ une fenetre de commande. * I l y a deux classes : la classe Com qui est la fenetre de commande * et qui permet de commander une autre classe A * Chaque classe a un lien vers l ’ autre afin de pouvoir communiquer . */ #include <TCanvas .h> #include <TGFrame.h> #include <TGButton .h> #include <TGComboBox.h> #include <TGTextEntry .h> #include <TGLabel .h> #include <TGProgressBar .h> #include <TPad.h> #include <TFrame.h> #include <TH1F.h> #include<TSystem .h> #include <TGNumberEntry.h> #include <TGSlider .h> #include <TGDoubleSlider .h> #include <TGTripleSlider .h> #include <TGMenu.h> #include <TApplication .h> #include <TRootEmbeddedCanvas .h> #include <thread> #include <chrono> #include <iostream> using namespace std ; class A; // classe definie aprˆ¤s * Description de la classe Com */ class Com: public TGMainFrame // ( derivee d une fenetre GUI) { public : TGMenuBar *fMB; // barre de menu TGPopupMenu *fM; // menu TGTextButton *fB ; // bouton TGTextEntry *fT ; // zone texte TGLabel *fL ; // texte fixe ecrit sur la fenetre TGComboBox *fC ; // l i s t e deroulante TGCheckButton *fCB; TGHProgressBar *fH ; // barre de progres TGNumberEntry *fNE; TGHSlider *fS ; TGDoubleVSlider *fS2 ; TGTripleHSlider *fS3 ; TRootEmbeddedCanvas *fEC ; // fenetre //??????????????????? A *a ; // lien vers un objet a d ’ une classe A //????????????????????? Com(A *a_i ); // constructeur ~Com( ) ; // destructeur Bool_t ProcessMessage (Long_t msg , Long_t parm1 , Long_t ); // fonction appe //========================================= // met a jour les valeurs de la fenetre }; /*!=============== * Une classe pour effectuer un calcul . . . */ class A { public : double x ; //????????????? void Calcul () { cout<<"calcul : " ; for (x=1; x<=50; x++) { this_thread : : sleep_for ( chrono : : milliseconds {100}); // attend 0.5 ms// attente , com?>Met_a_jour ( ) ; cout<<x<<","<<flush ; } cout<<endl ; } //???????????? Com *com; // pointeur vers l ’ objet fenetre de control void Lance_commandes() { TApplication theApp("App" , NULL,NULL); com= new Com( this ); // cree fenetre de control et l ’ associe a l ’ objet present theApp .Run( ) ; //????donne la main a l ’ u t i l i s a t e u r ??? } }; /*!===================== Constructeur */ Com: :Com(A *a_i) : TGMainFrame( gClient?>GetRoot () ,400 ,350) // t a i l l e de la f { a=a_i ; // i n i t i a l i s e le lien vers l ’ objet ˆ controller ( sera utile pour a?>com=this ; //???????????????? SetWindowName(" Fenetre de commandes "); // nom de la fenetre //????????? Menu ?????????? fMB = new TGMenuBar( this , 35 , 50 , kHorizontalFrame ); //?????boutons ???????????????????????? fB= new TGTextButton( this ,"S&tart " ,2); // cree bouton , l e t t r e t , et code message 2 fB?>Move(100 ,10); // position x , y fB?>SetToolTipText ("Demarre un calcul . . . " ) ; // texte d ’ aide fB?>Associate ( this ); fB?>Resize (80 , fB?>GetDefaultHeight ( ) ) ; // t a i l l e (x , y) //.. Label = texte fixe . . . . . . . . . . . . . . . . . fL= new TGLabel( this , new TGString(" Label = Texte fixe ")); fL?>Move(10 ,40); // position x , y / / . . . Zone de texte . . . . . . . . . . . . . . . . string text="toto "; fT = new TGTextEntry( this , text . c_str () ,1); //code 1 fT?>Move(10 ,70); // position x , y fT?>Resize (40 ,fT?>GetDefaultHeight ( ) ) ; // t a i l l e (x , y) char buf [100]=" texte "; fT?>Associate ( this ); //??????? Liste Combo ???? fC = new TGComboBox( this , 88); fC?>AddEntry(" entree 1" ,1); fC?>AddEntry(" entree 2" ,2); fC?>Resize (150 , 20);// t a i l l e (x , y) fC?>Select (2); // selection a priori AddFrame(fC , new TGLayoutHints(kLHintsTop | kLHintsLeft ,5 ,5 ,5 ,5)); // mis fC?>Move(10 ,100); // position //????? Progress Bars?????????????? fH?>ShowPosition (kTRUE,kFALSE," valeur : %.0 f "); //??????????? Sliders ??????????? fS = new TGHSlider( this ,150 , kSlider1 | kScaleDownRight , 2 ) ; // simple , Id=2 fS?>SetRange (0 ,50); fS?>SetPosition (39); fS?>Move(10 ,200); fS2 = new TGDoubleVSlider( this ,100 , kDoubleScaleNo , 3 ) ; //double fS2?>SetRange ( ?10 ,10); fS2?>Move(200 ,200); fS3 = new TGTripleHSlider ( this ,100 , kDoubleScaleBoth ,4 , // t r i p l e kHorizontalFrame ); fS3?>SetConstrained (kTRUE); fS3?>SetRange (0 ,10); fS3?>SetPosition (2 ,3); fS3 ?>SetPointerPosition ( 2 . 5 ) ; fS3?>Move(250 ,200); //???????? entree numerique ?????? double val =3.1415; fNE = new TGNumberEntry( this , val , 5 , 400 ,(TGNumberFormat : : EStyle ) 2); // fNE?>Associate ( this ); fNE?>Move(10 ,250); // position //????? Fenetre Canvas ?????????????? fEC = new TRootEmbeddedCanvas(" ec1 " , this , 200 , 100); AddFrame(fEC , new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX kLHintsExpandY , 5 , 5 , 5 , 5)); // les opti fEC?>Move(200 ,10); TCanvas *c = fEC?>GetCanvas ( ) ; c?>DrawFrame(0 ,0 ,1 ,1); c?>Update ( ) ; // c?>Modified ( ) ; / / . . . . Check Button fCB= new TGCheckButton( this , " : bouton check " ,420); fCB?>SetToolTipText (" texte d ’ aide pour bouton check "); fCB?>Associate ( this ); fCB?>Resize (120 , fCB?>GetDefaultHeight ( ) ) ; fCB?>Move(10 ,280); //???????????? MapWindow( ) ; // dessin de la fenetre } /*!==================================== * Destructeur */ Com::~Com() { delete fB ; delete fT ; delete fH ; delete fC ; } /*!==================================== * Fonction appelee lorsque l ’ u t i l i s a t e u r agit sur la fenetre de commandes * ?> I c i on gˆ¤re les actions */ Bool_t Com: : ProcessMessage (Long_t msg , Long_t p1 , Long_t p2) { int M = GET_MSG(msg) , S=GET_SUBMSG(msg ); cout<<" M = "<<M<<" S = "<<S<<" p1="<<p1<<" p2="<<p2<<endl ; switch (M) { case 1: switch (S) { case 3: switch (p1) { case 2: cout<<"bouton 2"<<endl ; a?>Calcul ( ) ; break ; } break ; case 4: i f (p1==420) { cout<<"bouton check . . "<<endl ; i f (fCB?>IsOn ()) cout<<" on."<<endl ; else cout<<" off ."<<endl ; } break ; } case 4: switch (S) { case 1: cout<<"texte change"<<endl ; switch (p1) { case 1: cout<<"Le nouveau texte est : "<<((fT?>GetBuffer())?>GetString break ; case 400: cout<<"Le nouveau c h i f f r e est : "<<((fNE?>GetNumber()))<<endl ; break ; } break ; } break ; default : break ; } Met_a_jour ( ) ; return kTRUE; } /*! ========================================= * Fonction appelˆ'e par une fonction externe * pour mettre a jour les valeurs de la fenetre , */ void Com: : Met_a_jour () { fH?>SetPosition (a?>x ); // progress Bar // gClient?>NeedRedraw(fH ); gSystem?>ProcessEvents ( ) ; // handle GUI events } /*!===Main================================================ */ int main( int argc , char **argv ) { A *a=new A; a?>Lance_commandes ( ) ; return 0; } Remarque 16.7.1. 1. fNumber->GetNumberEntry()->SetToolTipText("Whatever text you want to see here ") permet de rajouter un texte d’aide un widget. 16.7.2 Exemple simple avec un menu, des zones, des tabs et une fenetre secondaire /*! =========================== * avec Menu et Tab et sous fenetre libre */ #include <TCanvas .h> #include <TGFrame.h> #include <TGButton .h> #include <TGMenu.h> #include <TApplication .h> #include <TGTab.h> #include <iostream> using namespace std ; //???? i d e n t i f i e u r s ( int ) pour les messages de commandes enum Command_id { M_1, M_2, M_3, M_4, B_1, B_2 }; //==================================== // Fenetre secondaire class Fen2 : public TGTransientFrame { public : //???? constructeur Fen2( const TGWindow *p , const TGWindow *main , UInt_t w, UInt_t h , UInt_t options = kVerticalFrame ): TGTransientFrame(p , main , w, h , options ) { SetCleanup (kDeepCleanup ); //????????? zone avec bouton auto fFrame1 = new TGHorizontalFrame( this , 60 , 20 , kF auto fOkButton = new TGTextButton(fFrame1 , "&Ok" , B_1 fOkButton?>Associate ( this ); fFrame1?>AddFrame(fOkButton ); AddFrame(fFrame1 ); //????????? MapSubwindows ( ) ; Resize ( ) ; // resize to default size CenterOnParent ( ) ; // position r MapWindow( ) ; // fClient ?>WaitFor( this ); // otherwise canvas conte } //???? destructeur virtual ~Fen2 () { // Delete test dialog widgets . } //?????? virtual void CloseWindow () { cout<<"fenetre Fen2 fermee ¿?"<<endl ; // Add protection against double?clicks // fOkButton?>SetState ( kButtonDisabled ); DeleteWindow ( ) ; } //?????? virtual Bool_t ProcessMessage (Long_t msg , Long_t p1 , Long_t p2) { int M = GET_MSG(msg) , S=GET_SUBMSG(msg ); cout<<" Fen2 : M = "<<M<<" S = "<<S<<" p1="<<p1<<" p2="<<p2<<endl ; } }; /*!==========fenetre principales ======================== */ class Com: public TGMainFrame { public : int X=300, Y=150; // t a i l l e en pixels //===== Constructeur Com() : TGMainFrame( gClient?>GetRoot () , X,Y) { //?? regles de positionnement TGLayoutHints *fLH_TX = new TGLayoutHints(kLHintsTop TGLayoutHints *fLH_C = new TGLayoutHints( kLHintsCenter TGLayoutHints *fLH_L = new TGLayoutHints( kLHintsLeft , TGLayoutHints *fLH_R = new TGLayoutHints( kLHintsRight //????????? Menu ?????????? TGMenuBar *fMB = new TGMenuBar( this , X, Y);// , kHoriz AddFrame(fMB, fLH_TX); TGPopupMenu *fM=fMB?>AddPopup("&Menu1"); fM?>AddEntry("&Fenetre " ,M_1); fM?>AddSeparator ( ) ; fM?>AddEntry("O&ption 2" ,M_2) ; fM?>Associate ( this ); TGPopupMenu *fM2=fMB?>AddPopup("&Menu2"); fM2?>AddEntry("&Option 1" ,M_3) ; fM2?>AddSeparator ( ) ; fM2?>AddEntry("O&ption 2" ,M_4) ; fM2?>Associate ( this ); //????????? zone superieure auto * fFrame1 = new TGHorizontalFrame( this , X, Y);/ AddFrame(fFrame1 , fLH_L); TGButton *fOkButton = new TGTextButton(fFrame1 , "&O fOkButton?>Associate ( this ); fFrame1?>AddFrame(fOkButton , fLH_L); //.. bouton 2 TGButton *fOkButton2 = new TGTextButton(fFrame1 , "& fOkButton2?>Associate ( this ); fOkButton2?>Move(50 ,50); fFrame1?>AddFrame(fOkButton2 , fLH_L); //????? Tab ?????????????????? TGTab *fTab = new TGTab( this , X,Y); AddFrame(fTab ); //.. Tab1 ??????????? TGCompositeFrame * tf1 = fTab?>AddTab("Tab 1"); //.. bouton 1 TGTextButton * fB1 = new TGTextButton( tf1 , "&Bouton fB1?>Associate ( this ); tf1?>AddFrame(fB1 ); //.. bouton 2 TGTextButton * fB2 = new TGTextButton( tf1 , "&Bouton fB2?>Associate ( this ); tf1?>AddFrame(fB2 ); //.. Tab2 . . . . . . . . . . . . . . . . . . TGCompositeFrame * tf2 = fTab?>AddTab("Tab 2"); MapSubwindows ( ) ; Resize ( ) ; MapWindow( ) ; SetWindowName(" Fenetre de commandes "); // nom de la f } //=========================== ~Com() // destructeur { } //===================================== Bool_t ProcessMessage (Long_t msg , Long_t p1 , Long_t p2) { int M = GET_MSG(msg) , S=GET_SUBMSG(msg ); cout<<"Fen : M = "<<M<<" S = "<<S<<" p1="<<p1<<" p2="<<p2<<endl ; i f (M==1 && S==1 && p1 ==M_1 && p2== 0) new Fen2( fClient ?>GetRoot () , this , 400 , 200); return kTRUE; } }; // fin de la classe /*!===Main================================================ */ int main( int argc , char **argv ) { TApplication theApp("App" , nullptr , nullptr ); Com *com= new Com( ) ; // cree fenetre de control et l ’ associe a l ’ objet present theApp .Run( ) ; //????donne la main a l ’ u t i l i s a t e u r ??? return 0; } 16.7.3 Construire une interface de widgets la souris avec GuiBuilder Root propose un logiciel pour construire le code la souris : GuiBuilder. Cela permet au moins de voir la syntaxe du code pour rØaliser une construction. Voici comment l’utiliser. 16.8 Autres trucs 16.8.0.1 Nombre alØatoire avec Root Voir TRandom. #include <TRandom.h> gRandom->SetSeed(0); // initialise le hasard sur l’horloge double x= gRandom->Uniform(2); // nombre alØatoire dans l’intervalle (0,2) 16.8.0.2 Quelques Options gØnØrales de Root avec #include <TROOT.h> Mode Batch (sans sortie Øcran + rapide et permet de le lancer sur un serveur.) : gROOT->SetBatch(kTRUE); // met en mode Batch gROOT->IsBatch(); // permet de savoir si on est en mode batch. gROOT->SetStyle("Plain"); // style standard Rendre les dessins Øditables (modi ables) : gPad->SetEditable(0); // rend les dessins non modifiables gPad->SetEditable(1); // rend les dessins modifiables TCanvas *c=h->GetCanvas(); //Pour obtenir la fenetre TCanvas partir d’un histogramme TH1 *h gStyle->SetPadGridX(kTRUE); // grilles en X,Y gStyle->SetPadGridY(kTRUE); Chapitre 17 Mesure du temps On prØsente ici la classe chrono. Mais on peut aussi conseiller la classe 17.1 Mesure de la date et du temps ØcoulØ 17.1.0.1 Mesure tr?s prØcise du temps : #include <chrono> using namespace std::chrono; #include <iostream> using namespace std; int main() { auto t1=high_resolution_clock::now(); // mesure du "time point 1" // . instruction qui prend du temps .. for(int i=0;i<1e5;i++) cout<<" \t"<<i<<flush; // . auto t2=high_resolution_clock::now(); // mesure du "time point 2" auto dt1=duration_cast<nanoseconds>(t2-t1).count(); // calcul de la durØe en ns auto dt2=duration_cast<milliseconds>(t2-t1).count(); // calcul de la durØe en ms 163 CHAPITRE 17. MESURE DU TEMPS 164 cout<<"durØe: "<<dt1<< "ns"<<" = "<<dt2<< "ms"<<" = "<<dt3<<" s."<<endl; } A che : 0 1 2 3 99996 99997 99998 99999 durØe: 491264417ns = 491ms = 0.491264 s. 17.1.0.2 A chage de la date Au programme prØcØdent, si on rajoute : auto tt=system_clock::to_time_t(t1); // convertit au type time_t cout << "Maintenant c’est: " << ctime(&tt); On obtient : Maintenant c’est: Mon Aug 25 19:37:30 2014 17.2 Attendre 17.2.1 Attendre une certaine durØe #include <thread> #include <chrono> using namespace std::chrono; using namespace std::this_thread; sleep_for(seconds{2}); // attend 2 secondes sleep_for(milliseconds{3}); // attend 3 millisecondes 17.2.2 Attendre jusqu’ une certaine date #include <iostream> #include <thread> #include <chrono> using namespace std::chrono; using namespace std::this_thread; using namespace std; CHAPITRE 17. MESURE DU TEMPS 165 main () { auto t1 = high_resolution_clock::now(); // date actuelle cout<<" J’attends 3 secondes"<<endl; auto t2 = t1 + seconds {3}; // date future sleep_until(t2); // attente auto t3 = high_resolution_clock::now(); // date mesurØe auto dt = duration_cast<duration<double>>(t3-t1).count(); // mesure dt = t3-t1 cout<<"La durØe a ØtØ dt = "<<dt<<" s."<<endl; } On obtient : J’attends 3 secondes La durØe a ØtØ dt = 3.00016 s. Chapitre 18 IntØgrer des Øquations di Ørentielles ordinaires (EDO) avec la librairie ODEINT On propose d’utiliser la librairie odeint de boost. Voir aussi la documentation oø X (t) ? Rn est un vecteur qui dØpend du temps t ? R, et F : Rn × R ? Rn est une fonction connue. On connait X (0) et F l’on cherche X (t) pour d’autres valeurs de t ? R. Le thØor?me de Cauchy Lipchitz garantit l’existence et l’unicitØ de X (t) mais on a rarement une expression explicite de la solution. L’ordinateur est utile pour trouver des valeurs approchØes de X (t). Exemple Pour intØgrer avec la methode runge_kutta_dopri5 with standard error bounds 10-6 for the steps , le champ de vecteur dans X (t) = (x0 (t),x1 (t),x2 (t)) ? R3 du syst?me de Lorenz dØ ni par : avec les param?tres : ? = 10, R = 28, b = 8./3., on Øcrit : #include <iostream> #include <> #include <> 166 using namespace std; using namespace boost::numeric::odeint; const double sigma = 10.0; const double R = 28.0; const double b = 8.0 / 3.0; const int N=3; //nbre de variables de l’EDO /// - definition de la fonction intØgrer void lorenz( const array<double,N> &x , array<double,N> &dxdt , double t ) { dxdt[0] = sigma * ( x[1] - x[0] ); dxdt[1] = R * x[0] - x[1] - x[0] * x[2]; dxdt[2] = -b * x[2] + x[0] * x[1]; } /// int main() { array<double,N> x = { 10.0 , 1.0 , 1.0 }; // conditions initiales double t1 =0 ,t2=25; // temps initial et final double dt =0.1; //pas de temps for(double t=t1; t<=t2; t=t+dt) { integrate( lorenz , x , t, t+dt, dt); // intØgre entre t et t+dt cout << t << ’\t’ << x[0] << ’\t’ << x[1] << ’\t’ << x[2] << endl; } } Remarque 18.0.1. Pour plus d’options ou autres mØthodes d’intØgration voir la documentation. Exercice 18.0.2. Modi er le code ci-dessus pour dessiner la trajectoire dans le plan x1,x2 avec un point tous les pas de temps dt = 0.02 et t = 0 ? 100 et obtenir l’image suivante : #include <iostream> #include <boost/array . hpp> #include <boost/numeric/odeint . hpp> #include <TApplication .h> #include <TCanvas .h> #include <TMarker .h> #include <TH1F.h> using namespace std ; using namespace boost : : numeric : : odeint ; const double sigma = 10.0; const double R = 28.0; const double b = 8.0 / 3.0; const int N=3; //nbre de variables de l ’EDO ///? definition de la fonction intØgrer void lorenz ( const array<double ,N> &x , array<double ,N> &dxdt , double t ) { dxdt [ 0 ] = sigma * ( x [ 1 ] ? x [ 0 ] ); dxdt [ 1 ] = R * x [ 0 ] ? x [ 1 ] ? x [ 0 ] * x [ 2 ] ; dxdt [ 2 ] = ?b * x [ 2 ] + x [ 0 ] * x [ 1 ] ; } /// int main () { TApplication theApp("App" , nullptr , nullptr ); TCanvas *c = new TCanvas( "c " ," fenetre " ,500 ,500); // on precise la t a i l l e double xmin(?30),ymin (0) ,xmax(30) ,ymax(50); TH1F *h=c?>DrawFrame(xmin , ymin ,xmax,ymax ); // coordonnees de la fenetre ( h?>SetXTitle ("x1 "); h?>SetYTitle ("x2 "); int cpt=0; array<double ,N> x = { 10.0 , 1.0 , 1.0 }; // conditions i n i t i a l e s double t1 =0 , t2=100; // temps i n i t i a l et f i n a l double dt =0.002; //pas de temps for ( double t=t1 ; t<=t2 ; t=t+dt ) { integrate ( lorenz , x , t , t+dt , dt ); // integre entre t et t+dt // cout << t << ’\t ’ << x [ 0 ] << ’\t ’ << x [ 1 ] << ’\t ’ << x [ 2 ] << endl cpt++; TMarker p2(x [1] , x [ 2 ] , 8 ) ; p2 . SetMarkerColor (kRed ); p2 . SetMarkerSize (2); p2 .Draw ( ) ; i f ( cpt%10==0) c?>Update ( ) ; } c?>Update ( ) ; theApp .Run( ) ; } Chapitre 19 Lecture et Øcriture d’un chier son (audio) de format WAV 19.0.0.1 Introduction Un signal audio est une fonction t ? R ? s(t) ? R qui reprØsente les variations de pression sonores en fonction du temps t. Un chier audio au format WAV reprØsente le signal audio pour des valeurs discr?tes du temps : tj = j.?t, j = 1,2, avec un pas de temps ?t?1 appelØe frØquence d’Øchantillonage. Les valeurs standard sont : ?t?1 = 8000Hz, ?t?1 = 44100Hz, ?t?1 = 48000Hz, etc. Chaque valeur s(tj) est codØe sur 8 bits ou 16 bits. Le nombre de caneaux 1 ou 2 indique si le chier peut contient un (mode mono) ou deux signaux (mode stØrØo). Le chier WAV contient les valeurs s(tj) la suite, mais au dØbut du chier il y a les param?tres du codage. Regarder le code source pour plus d’informations. 19.0.0.2 Travail prØalable pour le projet C++ 1. Fichiers tØlØcharger et sauver dans son rØpertoire : bib_fred.h et . 2. Dans codeblocks, il faut ajouter ces chiers au projet en faisant dans le menu : Projet/add_ les 3. Il faut aussi rajouter -larmadillo dans les options de compilation (pour codeblocks c’est dans Setting/Compiler/LinkerSettings/OtherLinkerOptions) 19.0.1 Programme pour Øcrire un chier WAV : Pour tester, tØlØcharger ce chier et sauvegarder le dans le rØpertoire de votre projet. Le programme suivant permet 171 de lire et a cher les param?tres du codage audio de ce chier sonore s(tj) ? R (nombre de canaux, taux d’echantillonage etc..), de mettre les amplitudes s(tj) dans un vecteur de nombres de dessiner ce vecteur avec la librairie ROOT. #include <iostream> using namespace std; #include <TCanvas.h> #include <TMarker.h> #include <TApplication.h> #include "" ///-------------------------------------------int main() { TApplication theApp("App", nullptr, nullptr); //---- lecture audio du fichier wav avec le logiciel vlc system("vlc "); //--- lecture de tout le fichier vec signal; double f=Lecture_fichier_wav("", signal); // transfert les donnØes du fichier dans le vecteur double dt=1./f; // pas en temps //Dessin(signal, "signal"); Dessin(signal,"s","s",0,()*dt,"signal",0,"L",-1e4,1e4,kBlue,0); //--- lecture d’une partie vec signal2; double t1=4, t2=4.03; // intervalle de temps en secondes f=Lecture_fichier_wav("", signal2, 1,t1,t2); dt=1./f; //Dessin(signal2, "s2"); Dessin(signal2,"s","s",t1,()*dt,"signal2",0,"L",-1e4,1e4,kRed,0); (); } Sortie du programme : Remarquer que vous pouvez zoomer le dessin etc Sortie du programme sur le terminal : =====CrØe tableau partir du fichier: ChunkID=RIFF format=WAVE Nombre de canaux=1 Frequence d’echantillonage=48000 Hz Bits par echantillon=16 Nombre de donnØes =834402 Nombre d’octets par echantillon=2 Pas de temps dt =2.08333e-05 s. DurØe T =8.69169 s. Lecture fichier finie. -------------------- 19.0.2 Programme pour lire un chier WAV : Le programme suivant crØe un signal ØchantillonØ dans un vecteur de double (de type vec) et ensuite Øcrit ce vecteur dans un chier au format WAV. De plus il lance le logiciel vlc qui permet de jouer le chier (envoie sur la carte son). #include <iostream> using namespace std; #include <TCanvas.h> #include <TMarker.h> #include <TApplication.h> #include "" ///-------------------------------------------int main() { TApplication theApp("App", nullptr, nullptr); double dt=1./48000; // pas de temps en s. double T=5;// durØe en s. double f0=440; // frequence de la note que l’on veut en Hz for(int i=0; i<N; i++) { double t=i*dt; double f= f0 *(1+0.01*cos(2.*M_PI*t/1.)); // modulation de frequences double s= sin( 2*M_PI * f * t ); // note pure de frequence f s = s + 0.5 * sin( 2*M_PI * 2*f * t ); // rajoute harmonique 2 s = s + 0.3 * sin( 2*M_PI * 3*f * t ); // rajoute harmonique 3 signal(i) = s; } Dessin(signal,"s","s",0,()*dt,"signal",0,"L",-4,4,kBlue,0); // ecrit le vecteur signal dans le fichier . On indique dt Ecriture_fichier_wav(signal, dt,""); //---- lecture audio du fichier wav avec le logiciel vlc system("vlc "); (); } Sortie du programme : ce chier l’image du signal et la sortie sur terminal : =====CrØe un fichier wav partir du tableau Nombre de canaux=1 Frequence d’echantillonage=48000 Hz Bits par echantillon=16 Nombre d’octets par echantillon=2 Nombre d’ echantillons =240000 Pas de temps dt =2.08333e-05 s. DurØe T =5 s. Chapitre 20 Gestion de la carte son (audio temps rØel) avec la librairie sndio On utilisera la librairie dØveloppØe pour OpenBSD mais utilisable aussi avec Cette librairie permet de gØrer le son audio et aussi les Øv?nements midi en temps rØel (entrØe/sortie). Remarques Pour avoir la liste des cartes sons faire cat /proc/asound/cards ou aplay -l, cela donne par exemple 0 [HDMI ] : HDA-Intel - HDA Intel HDMI HDA Intel HDMI at 0xf7a1c000 irq 38 1 [PCH ] : HDA-Intel - HDA Intel PCH HDA Intel PCH at 0xf7a18000 irq 37 20.1 Installation ( faire la premi?re fois) Si vous Œtes administrateur, suivre cette Puis tester : Pour envoyer (Øcouter) le chier sur la carte 0 (carte son par default) par exemple il faut Øcrire : aucat -i . Si il n’y a pas de son et le message snd0: rsnd/0: failed to open audio device c’est que la carte son 0 n’existe pas. VØri er la liste des cartes sons de votre ordi avec la commande aplay -l et : Øcrire : aucat -f rsnd/1 -i . Si la carte 1 est la carte son par defaut (au lieu de la carte 0) il faut Øditer /etc/default/sndiod et ajouter l’option DAEMON_OPTS="-f rsnd/1". Voir remarques de con guration ci-dessous pour plus d’options. 176 Sinon si vous n’Œtes pas administrateur et que cette librairie n’est pas dØj installØe, il vous faut l’installer sur votre compte. Pour cela : TØlØcharger le source et dØcompresser l’archive. Dans le rØpertoire de sndio ainsi crØØ, taper : configure --prefix=$HOME make make install Remarque : cela a pour e et de compiler la biblioth?que et d’installer les librairies dans HOME/lib et HOME/include. Dans les options de compilation, il faudra rajouter : -I/$HOME/include ( : dans codeblocks, c’est rajouter dans Settings/Compiler/CompilerSetting/OtherOptions), -L/$HOME/lib et -lsndio ( : dans codeblocks, c’est rajouter dans Settings/Compiler/LinkerSettings/OtherOptions) Dans le chier .bashrc il faut rajouter export LD_LIBRARY_PATH=$HOME/lib et executer dans une fenŒtre de commande : . .bashrc Autres options de con guration de sndio : Dans le chier /etc/default/sndio on peut ecrire par exemple : DAEMON_OPTS="-z 128 -q rmidi/0 -q rmidi/1 -q rmidi/2 -f rsnd/1 -s default -m play,mon -s fred" puis faire service sndiod enable service sndiod start Rappel : amidi -l : enumere les ports midi utilises par les appareils" aplay -l : detecte les cartes son -> numero 1" Ces options signi ent : -q : signi e con gurer & exposer ce port midi pour que plusieurs programmes puissent l’utiliser" -z latence -f rsnd/1 : demande utiliser carte son numero 1. L’ordre est important. plus d’info avec : xterm -e sudo pasuspender -- SNDIO_DEBUG=2 etc.. -m play,mon -s fred : signi e redirige la sortie audio vers l’entree. Cela permet d’enregistrer ce qui est jouØ. Il y a une association entre les connecteurs entree et en sortie. 20.1.0.1 Travail prØalable pour le projet C++ 1. Fichiers tØlØcharger et sauver dans son rØpertoire : bib_fred.h et , audio.h et 2. Dans codeblocks, il faut ajouter ces chiers au projet en faisant dans le menu : Projet/add_ les 3. Il faut aussi rajouter -larmadillo dans les options de compilation (pour codeblocks c’est dans Setting/Compiler/LinkerSettings/OtherLinkerOptions) 4. DØmarrage du serveur sndiod, si vous Œtes administrateur, Øcrire dans un terminal : sudo sndiod -dd -z480 -f rsnd/0 et le laisser ouvert, sinon, si sndio a ØtØ installØ sur votre compte, Øcrire dans un terminal : sndiod -dd -z480 -f rsnd/0 et le laisser ouvert. 20.2 Utilisation du micro (entrØe) et du haut-parleur (sortie) 20.2.1 Le micro Le micro Øchantillonne le son avec un pas de temps Dt = 1/48000sec. et envoie les donnØes la carte son qui les convertit sous forme numØrique. Cet exemple lit les donnØes du micro depuis la carte son par bloc de N = 1000 donnØe, soit de durØe T = N.Dt = 0.02sec., et les dessine au fur et mesure. #include "" #include "" #include <iostream> using namespace std; #include <TROOT.h> #include <TApplication.h> //===Main================================================ int main() { TApplication theApp("App", nullptr, nullptr); Audio s; s.Initialise_audio(1); // initialise la carte son en entree (1) et affiche ses informations int N=1000; // on donne la taille que l’on veut (nombre d’echantillons) vec V(N); while(1) // boucle infinie { s.Lit_donnees_audio(V); // remplit V depuis le micro Dessin(V,"V"); // Dessin(V,"t","s",0,N*s.Dt,"V",0,"L",-1e4,1e4,kRed,0); // dessin normalisØ en rouge } /// ----Donne la main a l’utilisateur (); } 20.2.1.1 Exemple : dØtection de note de musique en temps rØel Periode : T=0.0034036s. <-> Frequence f=1/T=293.806 Hz Note =Re, Ecart = 0.00833494 demitons, Octave=0. #include "bib_fred . cc" #include "audio . cc" #include <iostream> using namespace std ; #include <TROOT.h> #include <TApplication .h> string Notes [12] = {"Do" ,"Do#", "Re" ,"Re#", "Mi" , "Fa" , "Fa#", "Sol " , "Sol#", //===Main================================================ int main () { TApplication theApp("App" , nullptr , nullptr ); Audio s ; s . Initialise_audio (1); // i n i t i a l i s e la carte son en entree (1) et affich int N=2000; // on donne la t a i l l e que l ’ on veut ( determine la fenetre tem vec V(N); //parametres pour la detection de la periode int NP=2; // 1 ,2: nbre de periodes afficher double f_A = 440; // diapason s . seuil_norme_par_I=1e4 ; s . seuil_C =0.1; s . verbose =0; //1 affiche messages erreur //????????????????????? while (1) // boucle i n f i n i e
{ //??? conversion en note de musique double nu = 12* log ( f /f_A)/ log (2.) + 69; int nu_i = floor (nu+0.5); cout<<" Note ="<<Notes [ nu_i%12]<<" Ecart = "<<nu?nu_i<<" demitons //???? affiche NP periodes extraites vec Extrait = V. rows ( t0/s .Dt , ( t0+NP*T)/ s .Dt ); // on extrait NP uword i = Extrait . index_max ( ) ; Extrait =s h i f t ( Extrait , ?i ); // place le max de l ’ extraitau debu Dessin ( Extrait ," t " ," s " ,0 ,NP*T," Extrait " ,0 ,"L",?1111,?1111,kRed , 0 ) } } /// ????Donne la main a l ’ u t i l i s a t e u r ??? theApp .Run( ) ; } 20.2.2 Le haut-parleur Voici un exemple qui lit les donnØes sur le micro et les envoie sur le haut-parleur apr?s un dØlai de 200ms ( la latence ). Mettre un casque. #include "bib_fred . cc" #include "audio . cc" #include <iostream> using namespace std ; #include <TROOT.h> #include <TApplication .h> int main () { TApplication theApp("App" , nullptr , nullptr ); Audio s ; s . Initialise_audio (3); // i n i t i a l i s e la carte son en entree et sortie (3 vec V; while (1) // boucle i n f i n i e { s . Lit_donnees_audio (V); // micro ?> V Dessin (V," t " ," s " ,0 ,V. size ()* s .Dt,"V" ,0 ,"L",?1e4 ,1 e4 , kRed , 0 ) ; // dessin s . Ecrit_donnees_audio (V); // V ?> Haut Parleur } /// ????Donne la main a l ’ u t i l i s a t e u r ??? theApp .Run( ) ; } Chapitre 21 Plusieurs programmes en parall?le qui communiquent, avec thread Il peut Œtre utile dans un projet de rØpartir le travail entre plusieurs programmes qui fonctionnent ensemble et communiquent. Un ordinateur est capable de gØrer plusieurs programmes qui fonctionnent. Si l’ordinateur ne poss?de qu’un processeur, il va faire avancer chaque programme tour tour en alternant sur des intervalles de temps tr?s court. Cela donne l’impression que plusieurs programmes fonctionnent en parall?le. On va s’interesser la communication entre ces programmes. On peut imaginer cela comme des travailleurs qui fabriquent une maison. Chacun travaille sur sa partie mais il y a des moments oø ils doivent Øchanger des informations, parfois un travailleur doit attendre qu’un autre ait ni son travail. Alors c’est le chef de projet (le processeur) qui se charge de superviser tout cela, de mettre en attente certains et rØactiver d’autres. Il y a donc trois concepts importants dont nous allons voir la mise en oeuvre : 1. Les threads : au niveau de la terminologie, chaque programme s’appelle un thread ou process ou context . On va tout d’abord apprendre dØmarrer des threads. 2. Les mutex : lorsque ces programmes doivent communiquer c’est dire Øchanger des donnØes (et c’est souvent le cas) il y a des prØcautions prendre a n que tout ce passe bien : par exemple il faut Øviter qu’une variable soit manipulØe par deux threads en mŒme temps. Ce sera le r le des mutex qui sont simplement des indicateurs booleen (vrai/faux) que l’on interpr?te comme (occupØ/libre). rØfØrences : article sur C++11 threads, locks and condition variables. 183 21.1 Lancement de threads ou processus #include <iostream> // std::cout using namespace std; #include <thread> //---- Une petite fonction qui affiche des a ---------------void f(int n) { for(int i=0;i<n;i++) cout<<"a"<<flush; cout<<endl; } //----Fonction principale ---------------int main() { // ici la fonction main est lancØe int n=10; thread t(f,n); // lance la fonction f(n) dans un thread cout << "Les fonctions main et f sont maintenant en concurrence.\n"; for(int i=0;i<n;i++) // affiche des b cout<<"b"<<flush; cout<<endl; t.join(); // attend ici que la fonction f soit finie cout << "f est finie.\n"; } 21.1.0.1 Commande de compilation : Si on utilise codeblocks, il faut rajouter -pthread -std=c++11 dans Settings/Compiler/Compiler_Settings/Other_Options et dans Settings/Compiler/Linker_Settings/Other_linker Pour une compilation en ligne de commande : g++ -o -pthread -std=c++11 RØsultat : Les fonctions main et f sont maintenant en concurrence. bbbbbaaaaaaaaaabbbbb f est finie. 21.1.0.2 Commentaires 1. Les fonctions f et main fonctionnent en parall?le et leur a chages ’a’ ou ’b’ sur l’Øcran se mØlangent de fa on incontrollØe. Un deuxi?me lancement du programme donnerait Øventuellement autre chose. 2. On peut passer (ou pas) des variables la fonction lancØe en parall?le, ici on passe la variable n. (a) On peut passer un pointeur (adresse d’une variable) la fonction f en Øcrivant par exemple, void f(int * pn) {..} pour la dØclaration et thread t(f,&n); pour le lancement.. Cela permet de transmettre un rØsultat de la fonction f vers la fonction main. 3. Il peut Œtre utile de crØer une certaine attente dans un programme. Utiliser Section 17.2. 4. Si vous oubliez t.join() la n, il y aura une erreur l’execution du programme. 5. Documentation compl?te sur 21.1.0.3 Lancer un thread d’une fonction membre de classe #include <iostream> using namespace std; #include <thread> class A { public: void f(int n ) { cout << n << endl; } }; int main() { A a; thread t1(&A::f, &a, 100); (); } Commentaires Si on lance le thread depuis une autre fonction membre, il faut bien sßr remplacer le pointeur &a par this. 21.1.0.4 Lancer une liste de thread (tableau) #include <iostream> // std::cout using namespace std; #include <thread> //---- Une petite fonction qui affiche n fois n ---------------void f(int n) { for(int i=0; i<n; i++) cout<<n<<flush; } //----Fonction principale ---------------int main() { int n=7; thread L_t[n]; // tableau de threads for (int i=0; i<n; i++) L_t[i] = thread(f,i); // lance de thread i qui affiche i fois i for(int i=0; i<n; i++) L_t[i].join(); // attend ici que le thread i soit fini } RØsultat : 315524563656666254443 21.1.0.5 Lancer une liste de thread (vector) MŒme chose que prØcØdement mais avec un vector. #include <iostream> // std::cout using namespace std; #include <thread> //---- Une petite fonction qui affiche n fois n ---------------void f(int n) { for(int i=0; i<n; i++) cout<<n<<flush; } //----Fonction principale ---------------int main() { int n=7; vector<thread> L_t; // tableau de threads for (int i=0; i<n; i++) L_t.push_back(thread(f,i)); // lance de thread i qui affiche i fois i for(int i=0; i<n; i++) L_t[i].join(); // attend ici que le thread i soit fini } 21.2 Mutex pour annoncer occupØ/libre 21.2.0.1 Introduction Un mutex est simplement une variable boolØenne dont on pense que la valeur est occupØ/libre . Une analogie possible est qu’un mutex est comme le panneau devant une salle de bain (sans serrure) qui annonce une valeur occupØ/libre . Par exemple, on peut utiliser un mutex avant de modi er une variable commune plusieurs threads. On dØclare un mutex par mutex mtx; Ensuite il y a deux commandes de base : 1. La commande (); qui signi e : si mtx est libre on le met occupØ et on continue, sinon, si il est occupØ alors on attend jusqu’ ce qu’il soit libre. 2. La commande mtx.unlock(); qui signi e : on met mtx libre 21.2.0.2 Remarques Quel intØret d’utiliser un mutex mtx plutot qu’une variable boolenne mtx ordinaire pour e ectuer la suite d’instructions si mtx est libre on le met occupØ et on continue, sinon on attend? rØponse : en utilisant une variable ordinaire, il y a le risque qu’un autre processus viennent modi er la valeur de la variable pendant cette suite d’instructions, ce que l’on ne souhaite absolument utilisant un mutex il est garantit que cette suite d’instruction est faite tout la suite par l’ordinateur, sans Œtre interrompu (on dit de fa on atomique ). 21.2.0.3 Exemple #include <iostream> // std::cout using namespace std; #include <thread> #include <mutex> mutex mtx; //-------------------void f(int n,char c) { (); // si mtx est libre on le bloque, sinon on attend for(int i=0;i<n;i++) cout<<c<<flush; cout<<endl; mtx.unlock(); // dØbloque mtx } //-------------------int main() { int n=10; thread t1(f,n,’a’); thread t2(f,n,’b’); (); (); } 21.2.0.4 RØsultat : aaaaaaaaaa bbbbbbbbbb 21.3 Communications : variables conditionnelles pour rØveiller un programme en attente Introduction Dans la communication entre programmes (process), il peut arriver qu’un programme 2 attende le resultat d’un autre(s) programme(s) 1. Voici une solution naive et maladroite pour programmer cette solution : Il y a une variable qui est a=0 au dØpart et que le programme met la valeur a=1 d?s qu’il a obtenu sont rØsultat. (Cette variable est protØgØe par un mutex). Le programme 2 e ectue une boucle in nie dans laquelle il lit la variable a et si a==1 alors il sort de la boucle et rØcup?re le rØsultat. Cette solution est maladroite car le programme 2 consomme du CPU (processeur de la machine) pour rien. Une meilleure solution est que le programme 2 soit mis en attente (en veille, il ne consomme pas de CPU) et rØveillØ l’arrivØe du rØsultat. Pour cela on utilise une variable conditionelle. Instructions de base : Une variable conditionnelle se dØclare par condition_variable cv; Il y a la commande (lck); qui met le thread en attente (endormi), et cv.notify_one(); qui reveille un parmi les autres thread qui se sont mis en attente, (ou cv.notify_all(); qui reveille tous les autres thread qui se sont mis en attente.) 21.3.0.1 Exemple de base oø un thread endormi est reveillØ par un autre. #include <iostream> using namespace std; #include <thread> #include <chrono> #include <mutex> #include <condition_variable> mutex mtx; condition_variable cv; //-------------------void f() { unique_lock<mutex> lck(mtx); // bloque le mutex mtx. Il sera debloque automatiquement par wait(lck), ou a la destruction de lck. (lck); // met le thread en attente (endormi). Il sera rØveillØ par un signal extØrieur. cout<<"FIN"<<endl; } //-------------------int main() { thread t(f); // reveille cv cv.notify_one(); // reveille le thread endormi // t.join(); } RØsultat : On attend 2sec. voil . FIN 21.3.0.2 Remarques L’instruction wait() sauvegarde tous les registres du processus (tout son environnement mØmoire) et notify_one() les restore. dans cet exemple l’utilisation du mutex ne sert rien, mais le langage C++ oblige l’utiliser dans l’instruction wait(lck). Voir ci-dessous l’exemple 21.3.4 oø l’utilisation du mutex est utile et obligatoire. Pourquoi faut il utiliser le mutex mtx? L’utilisation d’un mutex est obligatoire, car les thread se partagent une variable cv qui spØci e l’Øtat sommeil/reveillØ et donner l’instruction de rØveil. La fonction wait() utilise cette variable en faisant en fait de fa on sØquentielle : lecture de cv, si signal je dois me rØveiller. Exemple 21.3.1. oø un thread endormi attend des Øv?nements provenant de di erents thread extØrieurs . #include <iostream> using namespace std; #include <thread> #include <chrono> #include <mutex> #include <condition_variable> mutex mtx; condition_variable cv; // void f1() { while(1) { this_thread::sleep_for(chrono::milliseconds {1000}); cout<<"1"<<flush; cv.notify_one(); // reveille le thread main() endormi } } // void f2() { while(1) { this_thread::sleep_for(chrono::milliseconds {1502}); cout<<"2"<<flush; cv.notify_one(); // reveille les thread main() endormi } } // int main() { thread t1(f1); thread t2(f2); unique_lock<mutex> lck(mtx); // bloque le mutex mtx. Il sera debloque automatiquement par wait(lck), ou a la destruction de lck. while(1) { (lck); // met le thread main() en sommeil cout<<","<<flush; } // (); (); } RØsultat : 1,2,1,1,2,1,2,1,1,2,1,2,1,1,2,1,2,1,1,2, Remarque 21.3.2. 1,2,1,1,2,1,2,1,1,2,.. Plusieurs threads peuvent Œtre mis en attente par (lck); et la commande cv.notify_one(); va en reveillØ un seul (on ne sait pas lequel). Par contre la commande cv.notify_all(); reveillerait tous les threads mis en attente. Exemple 21.3.3. oø deux thread endormi sont reveillØs par une noti cation venant d’un 3eme thread . #include <iostream> using namespace std; #include <thread> #include <chrono> #include <mutex> #include <condition_variable> mutex mtx; condition_variable cv; // void f1() { unique_lock<mutex> lck(mtx); // bloque le mutex mtx. Il sera debloque automatiquement par wait(lck), ou a la destruction de lck. cout<<"a"<<endl; (lck); cout<<"b"<<endl; } // void f2() { unique_lock<mutex> lck(mtx); // bloque le mutex mtx. Il sera debloque automatiquement par wait(lck), ou a la destruction de lck. cout<<"A"<<endl; (lck); cout<<"B"<<endl; } // int main() { thread t1(f1); thread t2(f2); for(int t=3; t>=0; t--) // compte rebour { this_thread::sleep_for(chrono::seconds {1}); cout<<t<<","<<flush; // \r permet de re-ecrire au debut de la ligne } cout<<"partez!"<<endl; cv.notify_all(); // (); (); } Exemple 21.3.4. Un thread remplit une liste. Le deuxieme thread vide la liste . #include <iostream> using namespace std ; #include <thread> #include <chrono> #include <mutex> #include <condition_variable> #include <list > mutex mtx; condition_variable cv ; list <int> L; int N=10; //???????????????????? void f () { while (1) // boucle i n f i n i e { { } // i c i lck est detruit donc mtx est debloque mtx. lock ( ) ; cout<<"Liste pleine . On vide la l i s t e par le debut:"<<flush ; while (L. size ()>0) { cout<<L. front()<<","<<flush ; this_thread : : sleep_for ( chrono : : milliseconds {100}); L. pop_front ( ) ; // enleve le premier element } cout<<endl ; mtx. unlock ( ) ; cv . notify_one ( ) ; // r e v e i l l e le thread endormi } } //???????????????????? int main () { thread t ( f ); // lance la fonction f dans un thread while (1) // boucle i n f i n i e { mtx. lock ( ) ; // bloque le mutex i f (L. size ()==0) // si l i s t e vide { cout<<"Liste Vide . Remplit la l i s t e :"<<flush ; for ( int j =0; j< N; j++) { cout<<j <<","<<flush ; L. push_back( j ); this_thread : : sleep_for ( chrono : : milliseconds {100}); } cout<<endl ; } cv . notify_one ( ) ; // r e v e i l l e le thread endormi // se met en attente unique_lock<mutex> lck (mtx ); cv . wait ( lck ); } / / . . . . . . . . . t . join ( ) ; return 0; } RØsultat : Liste Vide. Remplit la liste:0,1,2,3,4,5,6,7,8,9, Liste pleine. On vide la liste par le debut:0,1,2,3,4,5,6,7,8,9, Liste Vide. Remplit la liste:0,1,2,3,4,5,6,7,8,9, CHAPITRE 21. PLUSIEURS PROGRAMMES EN PARALL¨LE QUI COMMUNIQUENT, AVEC THRE Liste pleine. On vide la liste par le debut:0,1,2,3,4,5,6,7,8,9, Chapitre 22 Messages MIDI Musical temps rØel avec la librairie sndio La convention sur les messages MIDI (Musical Instrument Digital Interface) entre ordinateurs sont apparus dans les annØes 80 pour la musique Ølectronique, l’informatique musicale et les synthØtiseurs. RØfØrence sur les messages MIDI. Dans ce chapitre nous voyons comment envoyer et/ou recevoir des messages midi depuis un programme en utilisant la librairie sndio. On verra aussi comment utiliser ces messages midi en musique. Con guration prØalable 22.1 Installation ( faire la premi?re fois) Suivre les instructions de la Section 20.1. De plus il faut tØlØcharger les chiers suivants et les sauver dans son rØpertoire : et . 1. Si vous utilisez codeblocks, il faut ajouter ces chiers au projet en faisant dans le menu : Projet/add_ les 2. Si vous utilisez un Make le, rajouter les chiers au projet selon les explications de la Section 14. 3. Il faut aussi rajouter -lsndio dans les options de compilation (pour codeblocks c’est dans Setting/Compiler/LinkerSettings/OtherLinkerOptions) 22.2 Exemple ØlØmentaire sur l’envoi et reception de messages MIDI 196 un contenu tr?s particulier qui ont une signi cation musicale comme dØbut de telle note , changement de volume ,.., et dans la Section suivante on prØcisera comment utiliser ces messages pour l’informatique musicale (reception des notes d’un clavier numØrique et envoie de notes un synthØtiseur). Instructions Copier et compiler les programmes et avec le programme Make le donnØs ci-dessous. Lancer le programme test_midi_in dans un terminal puis le programme test_midi_out dans un autre terminal. RØsultat : Le programme test_midi_in se met en attente et lorsqu’il recoit un message midi (provenant du programme test_midi_out) il l’a che l’Øcran : port ouvert. Attente de message .. 1 messages recus. Message recu sur le port=0: 144, 60, 50 Midi::On ferme les ports midi.. Le programme test_midi_out a che : port ouvert. J’ai envoye le message 144, 60, 50 sur le port 0 Midi::On ferme les ports midi.. Code C++ des programmes : Voici le code C++ ainsi que le Make le permet de compiler les deux programmes par l’instruction make all. //===== Programme test_midi_in . cc ========== #include <iostream> using namespace std ; #include "midi . h" int main () { Midi midi ; //??? ouvre un port midi en entree int port=0; // numero de port int res = midi . Initialise_port_midi_in ( port ); i f ( res==0) cout<<"port est encore ferme."<<endl ; else cout<<"port ouvert."<<endl ; //??? attente de messages cout<<"Attente de message .."<<endl ;
midi . Attente_Message ( ) ; cout<<"Messages recus."<<endl ; } //===== Programme test_midi_out . cc ========== #include <iostream> using namespace std ; #include "midi . h" int main () { Midi midi ; //??? ouvre port midi out int port = 0; int res = midi . Initialise_port_midi_out ( port ); i f ( res==0) { cout<<"port est encore ferme."<<endl ; exit (1); } else cout<<"port ouvert."<<endl ; //???? envoie message : 144 , 60 , 50 vector<unsigned char> message ; message . push_back (144); message . push_back (60); //?? ferme les ports midi ouverts midi . Ferme_port_midi_out( port ); } #=== Fichier Makefile LIBR= ?lm ?lpthread ?lsndio ?std=c++11 CC =g++ ?std=c++11 #========================================== OBJETS_MIDI_O= test_midi_out . o midi . o midi . o : midi . cc midi . h $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] test_midi_out . o : test_midi_out . cc bib_fred . h $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] test_midi_out : $(OBJETS_MIDI_O) $(CC) ?o test_midi_out $(OBJETS_MIDI_O) $(LIBR) #========================================== OBJETS_MIDI_I= test_midi_in . o midi . o midi . o : midi . cc midi . h $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] test_midi_in . o : test_midi_in . cc bib_fred . h $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] test_midi_in : $(OBJETS_MIDI_I) $(CC) ?o test_midi_in $(OBJETS_MIDI_I) $(LIBR) #========================== a l l : test_midi_out test_midi_in 22.3 Exemple d’envoie de message MIDI un synthØti- seur Le numØro de la note do est 60, ensuite do# = 61, etc Voir messages midi. Travail prØalable Installer un synthØtiseur MIDI comme midisyn et le lancer. Il sera alors l’Øcoute du port midithru/0 qui est le port 0 dans notre programme. Par exemple on lance midisyn dans une fenetre de commande part, par l’instruction : midisyn Brancher un piano Ølectrique ou autre instrument midi sur l’ordinateur, et observer le numØro de port par l’instruction amidi -l dans un terminal. Cela donne le numØro de port mettre dans le programme suivant d’apr?s la liste dans le chier midi.h. Par exemple rmidi/2 (port 2 de l’instruction amidi -l) est sur le port 4. Lancer le programme suivant qui va jouer la gamme chromatique. //===== Programme gamme. cc ========== #include <iostream> using namespace std ; #include "midi . h" int main () { Midi midi ; //??? ouvre port midi out int port = 0; int res = midi . Initialise_port_midi_out ( port ); i f ( res==0) { cout<<"port est encore ferme."<<endl ; exit (1); } else cout<<"port ouvert."<<endl ; / / . . . . . int ch = 0; // canal int inst = 0; // instrument , 0: piano midi . Program_Change( port , ch , inst );// associe l ’ instrument inst au c { / / . . . . . . . joue une note midi . . . . . . . . . . int vol =100; cout<<"joue note "<<key<<endl ; midi . Joue_note( port , ch , key , vol ); // message pour jouer une midi . Sleep (200); // attente en ms / / . . . . eteind une note midi . . . . . . . . . midi . Eteind_note ( port , ch , key ); // message pour eteindre la } //?? ferme les ports midi ouverts midi . Ferme_port_midi_out( port ); } #====== Fichier Makefile====================================
OBJETS_MIDI_G= gamme. o midi . o midi . o : midi . cc midi . h $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] gamme. o : gamme. cc $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] gamme: $(OBJETS_MIDI_G) $(CC) ?o gamme $(OBJETS_MIDI_G) $(LIBR) Remarque 22.3.1. La liste des codes des instruments est donnØe d’apr?s la convention general midi . Le canal 9 est rØservØ aux percussions. Mais si vous utilisez le synthetiseur midisyn, pour le moment il n’y a que les possibilitØs suivantes : Le dØtail des sons est con gurable dans les chiers etc On peut par exemple rajouter de nouveaux sons en ajoutant les chiers raw correspondant et un chier de con guration associØ. //===== Programme gamme. cc ========== #include <iostream> using namespace std ; #include "midi . h" int main () { Midi midi ; //??? ouvre port midi out int port = 0; int res = midi . Initialise_port_midi_out ( port ); i f ( res==0) { cout<<"port est encore ferme."<<endl ; exit (1); } else cout<<"port ouvert."<<endl ; / / . . . . . int ch = 0; // canal int inst = 0; // instrument , 0: piano midi . Program_Change( port , ch , inst );// associe l ’ instrument inst au c for ( int key=60; key<=72; key++) //montee de la gamme chromatique { / / . . . . . . . joue une note midi . . . . . . . . . . int vol =100; cout<<"joue note "<<key<<endl ; midi . Joue_note( port , ch , key , vol ); midi . Sleep (200); // attente en ms / / . . . stick midi . Joue_note( port , 9 , 37 , vol ); midi . Sleep (200); // attente en ms / / . . . . eteind une note midi . . . . . . . . . midi . Eteind_note ( port , ch , key ); } / / . . . splash midi . Joue_note( port , 9 , 55 , 100); //?? ferme les ports midi ouverts midi . Ferme_port_midi_out( port ); } 22.4 Reception de notes midi depuis un piano Ølectrique Comme application de l’exemple prØcØdent, le programme suivant ecoute les messages midi et les analyse. Si il reconnait Note on et Note o , il a che les notes identi Øes. Travail prØalable Brancher un piano Ølectrique ou autre instrument midi sur l’ordinateur, et observer le numØro de port par l’instruction amidi -l dans un terminal. Cela donne le numØro de port mettre dans le programme suivant d’apr?s la liste dans le chier midi.h. Par exemple rmidi/2 (port 2 de l’instruction amidi -l) est sur le port 4. //===== Programme reception_notes . cc ========== #include <iostream> using namespace std ; #include "midi . h" int main () { Midi midi ; //??? ouvre un port midi en entree int port=0; // numero de port int res = midi . Initialise_port_midi_in ( port ); i f ( res==0) cout<<"port est encore ferme."<<endl ; else cout<<"port ouvert."<<endl ; //??? attente de messages midi . Attente_Message ( ) ; //???analyse des messages recus for ( auto message : midi . L_message) octet . I l reste le message midi standard i f ((( int ) message [0])/16 == 0x9 && (( int ) message [ 2 ] ) > 0 ) // note on avec vel >0 { int ch=(int ) message [0] ?0x90 ; int key=(int ) message [ 1 ] ; int vel=(int ) message [ 2 ] ; cout<<"Note on recue : port="<<port_in<<" canal="<<ch } else i f ((( int ) message[0])/16==0x8 | | ((( int ) message [0])/1 // note off { int ch=(int ) message [ 0 ] ; i f (ch/16==0x8) ch?= 0x80 ; else i f (ch/16==0x9) ch?= 0x90 ; int key=(int ) message [ 1 ] ; int vel=(int ) message [ 2 ] ; cout<<"Note off recue : port="<<port_in<<" canal="<<c } } //??? ferme port midi midi . Ferme_port_midi_in( port ); } #========================================== OBJETS_MIDI_R= reception_notes . o midi . o midi . o : midi . cc midi . h $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] reception_notes . o : reception_notes . cc $(CC) $(CFLAGS) ?c $ *. cc ?o [email protected] reception_notes : $(OBJETS_MIDI_R) #========================== a l l : reception_notes Chapitre 23 Messages MIDI (Musique) sur chier Les messages musicaux MIDI peuvent Œtre Øcrit dans un chier. Ce format s’appelle Standard Midi File S.M.F. ref : ici ou ici ou la rØfØrence. La convention des messages est tr?s similaire aux messages en temps rØel. Il y a peu de di Ørences (il y a Message mØta Øv?nement et plus de Message temps rØel ) 23.1 Structure d’un chier SMF Il est constituØ de blocs de donnØes appelØs chunk (=gros morceau). Le premier bloc annonce les suivants. Conventions sur le codage des nombres : CLF Codage longueur xe . Un nombre entier positif X est codØ sur n octets x1xn avec xj ? [0,255] au format big-endian, c’est dire que le nombre associØ X est X = x1256n?1 + x2256n?2 + xn?1256 + xn Sauf mention du contraire on utilise CLF. CLV Codage longueur variable : pour n octets x1,x2, xn on utilise seulement les 7 derniers bits de chaque octet. Le premier bit quand lui est 1 (et donc octet xj ? 128) pour les octets 0 ? j < n, et pour le dernier octet utilisØ j = n le premier bit est 0 (et donc octet xn < 128). Le nombre associØ X est (format big-endian) X = x˜1128 + x˜n?1128 + xn, avec x˜j = xj ? 128 23.1.1 Premier bloc (header chunk) Il est composØ de 4 octets : MThd = 0x4D546864. Ces 4 caract?res invariables en dØbut de chier signalent que c’est un chier MIDI. 207 L : 4 octets : 0x00000006 : longueur invariable en octets de ce bloc, comptØe apr?s ces 4 octets. C : 2 octets : 0x0000 : si le chier contient un seul bloc apr?s celui ci. Il contiendra Øventuellement plusieurs canaux (pour avoir polyphonie) 0x0001 : (le plus courant) si le chier contient plusieurs blocs apr?s celui ci. Ils seront jouØs simultanØment. (polyphonie). Les mØtadonnØes sont sur le premier bloc. N : 2 octets : nombre de blocs qui vont suivre ce premier bloc. F : 2 octets : Delta time ou unitØ de temps . Si le premier bit de F est 0, cad F < 215 = 32768, alors l’unitØ de temps est oø T qui est le tempo cad la durØe entre deux pulsation. ex : est Andante donnØe apr?s dans les mØta donnØes (ou pas donnØe). En gØnØral, F = 120,240,480,960 Si le premier bit de F est 1, cad F ? 215 = 32768, alors F est codØ en SMPTE compatible units. Midi time code cad F = (100bbbbb cccccccc)base2 avec (bbbbb)base2 est le nombre d’images par secondes (ex : 24,25,29,30) et (cccccccc)base2est le nombre de subdivisions d’une image ( frame ) 23.1.2 Bloc de piste (Track chunk) Il est composØ de 4 octets : MTrk = 0x4D54726B L : 4 octets : longueur en octets de ce bloc. (comptØs apr?s ces 4 octets) Une suite d’ Øv?nements midi (Midi event). Chaque Øv?nement midi est composØ de Nt : nombre de pas de temps ?t ØcoulØs depuis l’Øv?nement prØcØdant, codØ au format CLV. M : Un message midi (le nombre d’octets dØpend du premier quartet) : Si le premier quartet q (4 bits) a le premier bit 1, cad q ? 8 alors M a 2 ou 3 octets. Si le premier quartet q (4 bits) a le premier bit 0, cad q < 8 alors M a deux octets, c’est dire que la derni?re action est rØpØtØe, meme 1er octet, running status . Par exemple pour coder l’accord C-E-G sur le canal 3 (x00 - x93 - x3C - x40) (x00 - x93 - x40 - x40) (x00 - x93 - x43 - x40) : sans running status ou (x00 - x93 - x3C - x40) (x00 - x40 - x40) (x00 - x43 - x40) : avec running status La n de bloc est annoncØe par le message avec 3 octets: xFF, x2F, x00 23.1.3 Message Midi standard M M a 2 ou 3 octets M = x1,x2,x3. Le premier octet est dØcomposØ en 2 quartet (4 bits chacuns) x1 = qc oøc ? [0,15] est le numØro de canal. Les octets suivants sont
0 ? x1,x2< 128 23.1.4 Message mØta Øv?nement M (pour les signaux midi sur chier et non pas temps rØel)
23.1.5 Message du syst?me (System Exclusive Message (SysEx)) M
Le message se termine par le mŒme octet 0xF7 : 0xF0 + <data_bytes> 0xF7 ou: 0xF7 + <data_bytes> 0xF7 23.1.6 Message temps rØel M (pour les signaux midi temps rØel et non sur chier) 23.1.7 Control Change pour les message x1 = xBc (copiØ de jchr) Selon la valeur de x2= type : x00 Bank Select : sØlection d’une banque de sons x01 Modulation Wheel : roulette de modulation ( 0 si rØinitialisation)
x02 Breath Controller : contr le par le sou e x04 Foot Controller : contr le au pied x05 Portamento Time : rapiditØ du portamento x06 Data Entry MSB : octet fort de donnØe supplØmentaire x07 Channel Volume : volume principal d’un canal x08 Balance x0A Pan : panoramique, 64 est le milieu x0B Expression Controller : contr le de l’expression (127 si rØinitialisation) x0C E ect Control 1 : e et 1 x0D E ect Control 2 : e et 2 x10 x13 General Purpose 1-4 : contr leurs tout usage x20 x3F octet de poids faible pour les contr leurs 0-31 x40 Damper Pedal : maintient toutes les notes (0 si rØinitialisation) x41 Portamento On/O (0 si rØinitialisation) x42 Sostenuto On/O : sustain pour la note jouØe au moment du dØclenchement (0 si rØinitialisation) x43 Soft Pedal On/O : pØdale douce (0 si rØinitialisation) x44 Legato Footswitch : contr le de legato au pied x45 Hold 2 : autre sustain x46 Sound Controller 1 / Sound Variation : variation du timbre x47 Sound Controller 2 / Timbre/Harmonic Intens. : contenu harmonique du timbre x48 Sound Controller 3 / Release Time : temps de release par dØfaut x49 Sound Controller 4 / Attack Time : temps d’attaque x4A Sound Controller 5 / Brightness : brillance x4B Sound Controller 6 / Decay Time : temps d’extinction du son de param?tre non enregistrØ x63 NRPN / Non-Registered Parameter Number - MSB : octet de poids fort de numØro de param?tre non enregistrØ x64 RPN / Registered Parameter Number - LSB : octet de poids faible de numØro de param?tre enregistrØ x65 RPN / Registered Parameter Number - MSB : octet de poids fort de numØro de param?tre enregistrØ x78 - x00 All Sound O : Øteint tous les sons, mŒme les ns d’enveloppe x79 - x00 Reset All Controllers : rØinitialise tous les contr les x7A - xnn Local Control On/O : dØsactive (x00) ou rØactive (x7F) la production du son par le clavier ma tre x7B - x00 All Notes O : Øteint toutes les notes (mais pas les enveloppes), comme le font les quatre suivant x7C - x00 Omni Mode O - recommandØ explanations x7D - x00 Omni Mode On : envoie les ØvØnements sur tous les canaux x7E - xnn Mono Mode On : ne permet qu’une note la fois sur un canal, ce qui permet de mieux simuler la production de son d’un instrument monophonique (portamento); xnn est le nombre de canaux??? x7F - x00 Poly Mode On : permet plusieurs notes simultanØes sur un mŒme canal 23.1.8 type pour les Meta-Evenement Un meta-evenement contient 0xFF 1 octet : meta-type event : (cf cidessous) 1 octet L : length of meta event data ou en codage CLV? the actual event data. Par exemple: 3 octets: xFF, x2F, x00 en n de bloc.
23.1.9 Systeme exclusif Messages (Sysex) Constructeurs Ces identi ants servent lors de messages spØci ques au matØriel (SysEx, DLS and XMF). Les membres historiques du MMA disposent d’un identi ant d’un octet; les plus rØcents consistent en deux octets prØ xØs de l’octet nul. x00 (prØ xe) x0E Matthews Research x24 Hohner x46 Kamiya Studio x01 Sequential Circuits x10 Oberheim x25 Crumar x47 Akai x02 Big Briar x11 PAIA x26 Solton x48 Victor x03 Octave / Plateau x12 Simmons x27 Jellinghaus Ms x4B Fujitsu x04 Moog x13 DigiDesign x28 CTS x4C Sony x05 Passport Designs x14 Fairlight x29 PPG x4E Teac x06 Lexicon x15 JL Cooper x2F Elka x50 Matsushita x07 Kurzweil x16 Lowery x36 Cheetah x51 Fostex x08 Fender x17 Lin x3E Waldorf x52 Zoom x09 Gulbransen x18 Emu x40 Kawai x54 Matsushita x0A Delta Labs x1B Peavey x41 Roland x55 Suzuki x0B Sound Comp. x20 Bon Tempi x42 Korg x56 Fuji Sound x0C General Electro x21 S.I.E.L. x43 Yamaha x57 Acoustic Technical Lab. x0D Techmar x23 SyntheAxe x44 Casio Chapter 24 Autres librairies conseillØes (?) Trinilos Math Kernel Library 24.1 alg?bre linØaire SimuNova Eigen pour l’alg?bre linØaire. 24.2 Images, vidØos, graphisme et boites de dialogues pour le graphisme de base (avec OpenGL) et le son audio. Librairie bien adaptØ pour les jeux. pour les boites de dialogues et le graphisme. Biblioth?que lØg?re et performante. biblioth?que tr?s gØnØrale, performante, multiplateformes mais assez complexe. 213 Chapitre 25 Le traitement d’images et de vidØos avec la librairie OpenCV est spØcialisØe pour le traitement d’image et de vidØos (CV signi e ComputerVision ). Un Tutoriel. Livre conseillØ : OpenCV by example de P. Joshi, D.M. Escriva et V. Godoy. 25.1 Commande de compilation Avant d’Øcrire un programme utilisant les librairies de OpenCV, il faut, une fois pour toutes, Dans Settings/Compiler/Linker_Settings/Other_linker_Options, rajouter : ‘pkg?config ??cflags ??l i b s opencv ‘ Remarque 25.1.1. Si vous n’utilisez pas codeblocks, mais Make le par exemple, la compilation en ligne de commande du programme pour donner l’executable main est : g++ main . cpp ?o main ?g ?std=c++11 ‘pkg?config ??cflags ??l i b s opencv ‘ 25.2 Lecture et ecriture de chiers images Les commentaires expliquent les fonctions. Il faut avoir le chier dans le rØpertoire du programme. #include <iostream> using namespace std ; 214 #include <string > #include <opencv2/core/core . hpp> #include <opencv2/highgui/highgui . hpp> using namespace cv ; //========================= int main () {
/ / . . . . . Lecture d ’ un pixel int x=img . cols /2; // selectionne point (x , y) au centre de l ’ image int y=img . rows /2; Vec3b p= img . at<Vec3b>(x , y ); cout << "La valeur du pixel en x="<<x<<", y="<<y<<" est (B,G,R) : (" << ( int )p / / . . . . . dessin de l ’ image imshow("Image" , img ); } Resultat : A che deux images : une en couleur et l’autre en noir et blanc. Pixel value (B,G,R): (3,7,12) 25.2.0.1 Fonctions utiles sur les images Copie d’une image : La commande Mat image2 = image1; ne fait pas une copie des images mais seulement de l’adresse (c’est dire que image1 et image2 vont dØsigner la mŒme image). Pour dupliquer l’image il faut faire image1.copyTo(image2); Recadrage d’une image : resize(frame, frame, Size(), scalingFactor, scalingFactor, INTER_AREA); Mat img= imread("", 0); // 0 : convertit en gris 25.3 Utilisation de la souris Le programme suivant montre comment utiliser la souris. Dans cet exemple on selectionne une partie de l’image avec clic gauche (down/up) et on revient l’image de dØpart avec clic droit (down). Le codesuivant utilise le chier image tØlØcharger dans le mŒme rØpertoire. #include <iostream> using namespace std ; #include <string > #include <opencv2/core/core . hpp> #include <opencv2/highgui/highgui . hpp> using namespace cv ; //??????????????????????????????? Mat img , image , roi ; int etat_select = 0;// 0: selection pas faite , 1: selection en cours , 2: sele Rect selection ; // t a i l l e de la selection Point origin ; // point de depart de la selection //???? fonction appelee si la souris est actionnee static void onMouse( int event , int x , int y , int , void* ) { // cout<<"event="<<event<<" x="<<x<<" y="<<y<<endl ; //??? si action du c l i c souris switch ( event ) { i f ( etat_select==0) { origin = Point (x , y ); selection = Rect (x , y ,0 ,0); etat_select = 1; } break ; case 4: // c l i c gauche up i f ( etat_select==1 && selection . width > 0 && selection . height > 0 ) { etat_select =2; // selection f i n i e bitwise_not ( roi , roi ); // inversion de roi ( dans imag imshow("Image" , roi ); } break ; case 2: // c l i c droit down i f ( etat_select==2) { etat_select = 0; imshow("Image" , img ); } } //??? si deplacement souris , traitement de l ’ image i f ( etat_select == 1 ) { selection . x = MIN(x , origin . x ); selection . y = MIN(y , origin . y ); selection . width = std : : abs (x ? origin . x ); selection . height = std : : abs (y ? origin . y ); selection &= Rect (0 , 0 , img . cols , img . rows ); // pour ne pas depasser i f ( selection . width > 0 && selection . height > 0 ) { img . copyTo( image ); roi = Mat(image , selection ); // la matrice roi est un bitwise_not ( roi , roi ); // inversion de roi ( dans imag } } } //========================= int main () { / / . . . . . Lecture des images img= imread ("M2. png "); / / . . . . . Affiche l ’ image a l ’ ecran namedWindow( "Image" , 1 ); setMouseCallback ( "Image" , onMouse , 0 ); // associe la fonction de lecture de imshow("Image" , img ); waitKey (0); } 25.4 Lecture et Øcriture d’un chier video ou de la webcam #include <iostream> #include <string > #include <sstream> using namespace std ; #include "opencv2/core . hpp" #include "opencv2/highgui . hpp" using namespace cv ; //????????????????????????????????????? int main () { / / . . . . ouverture du f i c h i e r video ou webcam String nom = "video_pendule .mp4"; // f i c h i e r video VideoCapture cap ; cap . open(nom); //cap . open (0);// ouvre la webcam. i f (! cap . isOpened ()) // si erreur d ’ ouverture return ?1; / / . . . ouverture d ’ un f i c h i e r video en ecriture String nom2= "video_pendule2 . avi "; // f i c h i e r video Size frameSize ( static_cast<int >(cap . get (CV_CAP_PROP_FRAME_WIDTH)) , static_ VideoWriter out (nom2, CV_FOURCC( ’P’ , ’ I ’ , ’M’ , ’1 ’) , 20 , frameSize , true ); i f ( ! out . isOpened () ) return ?1; / / . . . . boucle i n f i n i e while ( true ) { i f (waitKey (30) >= 0) break ; // Montre dessin . Attente de 30 ms et arr } cap . release ( ) ; // libere la memoire allouee } Remarque 25.4.1. La commande waitKey(0) permettrait de faire avancer la video image par image. 25.5 DØtection d’objet par leur couleur Voici un exemple de programme qui lit une image et selectionne les objets bleus. Pour a cher les images intermediares, il faut dØcommenter les lignes correspondantes. Voici une documentation sur les formats de couleurs. On utilise le format HSV (voir aussi HSV sur wikipedia) qui est proche de la perception humaine : H ? [0,1] est la teinte de la couleur, par exemple H ? [0.5,0.7] est une palette de bleus. S ? [0,1] est la saturation entre S = 0 (blanc) et S = 1 (couleur vive). V ? [0,1] est la luminositØ entre V = 0 (noir) et V = 1 Sauver l’image ci-dessous dans le rØpertoire du programme. RØsultat du programme : #include <iostream>using namespace std;#include <string> #include < 25.6 DØtection et suivit d’objet de couleur avec l’algorithme CamShift Voici des informations, sur les algorithmes MeanShift et CamShift , ou sur Le code ci-dessous est tirØ du site web de opencv. #include "opencv2/video/ tracking . hpp" #include "opencv2/imgproc/imgproc . hpp" #include "opencv2/highgui/highgui . hpp" #include <iostream> #include <ctype .h> using namespace cv ; using namespace std ; //??????????????????????????????? Mat image ; bool backprojMode = f a l s e ; // true : si on montre l ’ image s e u i l l e e bool showHist = f a l s e ; // true : on montre l ’ histogramme Rect selection ; // t a i l l e de la selection Point origin ; // point de depart de la selection //???? Si l ’ u t i l i s a t e u r selection un rectangle ?> selection et le flag trackO static void onMouse( int event , int x , int y , int , void* ) { // cout<<"event="<<event<<" x="<<x<<" y="<<y<<endl ; i f ( selectObject ) { selection . x = MIN(x , origin . x ); selection . y = MIN(y , origin . y ); selection . width = std : : abs (x ? origin . x ); selection . height = std : : abs (y ? origin . y ); selection &= Rect (0 , 0 , image . cols , image . rows ); // pour ne pas depas } switch ( event ) { cout<<"event="<<event<<endl ; case CV_EVENT_LBUTTONDOWN: // coin de depart origin = Point (x , y ); selection = Rect (x , y ,0 ,0); selectObject = true ; break ; case CV_EVENT_LBUTTONUP: selectObject = f a l s e ; i f ( selection . width > 0 && selection . height > 0 ) trackObject = ?1; break ; } } //??????????????????????????????? static void help () { cout << "\nThis is a demo that shows mean?s h i f t based tracking \n" "Usage : \n" " ./ camshiftdemo \n"; cout << "\n\nHot keys : \n" "\tESC ? quit the program\n" "\ tc ? stop the tracking \n" "\tb ? switch to/from backprojection view\n" "\th ? show/hide object histogram\n" "\tp ? pause video\n" "To i n i t i a l i z e tracking , select the object with mouse\n"; } ///??????????????????????????????? int main( int argc , const char** argv ) { help ( ) ; VideoCapture cap ; Rect trackWindow ; int hsize = 16; // pour histogramme float hranges [ ] = {0 ,180}; const float * phranges = hranges ; String videoFile= "pendule_jaune .mp4"; // f i c h i e r video // cap . open( videoFile ); // ouvre le f i c h i e r video cap . open (0); // ouvre camera i f ( ! cap . isOpened () ) { cout << "***Could not i n i t i a l i z e capturing ***\ n"; return ?1; } namedWindow( "Histogram " , 0 ); namedWindow( "CamShift Demo" , 0 ); setMouseCallback ( "CamShift Demo" , onMouse , 0 ); // associe la fonction d / / . . . . pose les s l i d e r s int vmin = 10 , vmax = 256 , smin = 30; createTrackbar ( "Vmin" , "CamShift Demo" , &vmin , 256 , 0 ); createTrackbar ( "Vmax" , "CamShift Demo" , &vmax, 256 , 0 ); createTrackbar ( "Smin" , "CamShift Demo" , &smin , 256 , 0 ); Mat frame , hsv , hue , mask , hist , histimg = Mat : : zeros (200 , 320 , CV_8UC3) , bool paused = f a l s e ; //?????? boucle i n f i n i e ?????????? for ( ; ; ) { i f ( ! paused ) { cap >> frame ; i f ( frame . empty () ) break ; } frame . copyTo( image ); // ?> image i f ( ! paused ) { cvtColor (image , hsv , COLOR_BGR2HSV); // convertit ?> hsv i f ( trackObject ) { int _vmin = vmin , _vmax = vmax; // seuillage ?> mask ( fenetre de 0 et 1) inRange (hsv , Scalar (0 , smin , MIN(_vmin,_vmax)) , Scalar (180 , 2 //imshow( "mask" , mask ); // Mix the specified channels int ch [ ] = {0 , 0}; hue . create ( hsv . size () , hsv . depth ( ) ) ; // ?> hue : nouvelle fene mixChannels(&hsv , 1 , &hue , 1 , ch , 1); // ?> copie hsv dans hu // on traite la selection ?> histogramme i f ( trackObject < 0 ) { cout<<"i c i"<<endl ; // imshow( "hue" , hue ); //imshow( "mask" , mask ); //waitKey (0); // Compute the histogram and normalize it ?> tableau hist calcHist(&roi , 1 , 0 , maskroi , hist , 1 , &hsize , &phranges ) normalize ( hist , hist , 0 , 255 , CV_MINMAX); trackWindow = selection ; trackObject = 1; histimg = Scalar : : a l l (0); int binW = histimg . cols / hsize ; Mat buf (1 , hsize , CV_8UC3); for ( int i = 0; i < hsize ; i++ ) buf . at<Vec3b>(i ) = Vec3b( saturate_cast<uchar>(i *180./ cvtColor ( buf , buf , CV_HSV2BGR); // dessin des rectangles de l ’ histogramme sur la fenetre for ( int i = 0; i < hsize ; i++ ) { int val = saturate_cast<int >(hist . at<float >(i )* histim rectangle ( histimg , Point ( i *binW, histimg . rows ) , Point } } // Compute the histogram backprojection calcBackProject(&hue , 1 , 0 , hist , backproj , &phranges ); // ?> backproj &= mask ; // ameliore backproj / / . . . trackwindow , backproj ?> trackBox RotatedRect trackBox = CamShift ( backproj , trackWindow , TermCriteria ( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10 , 1 ) ) ; // Check i f the area of trackingRect is too small i f ( trackWindow . area () <= 1 ) { i f ( backprojMode ) cvtColor ( backproj , image , COLOR_GRAY2BGR ); // draw rotated rectangle trackBox Point2f rect_points [ 4 ] ; trackBox . points ( rect_points ); RNG rng (12345); Scalar color = Scalar ( rng . uniform (0 , 255) , rng . uniform (0 ,255 for ( int j = 0; j < 4; j++ ) line ( image , rect_points [ j ] , rect_points [( j +1)%4], color , // Draw the e l l i p s e on top of the image e l l i p s e ( image , trackBox , Scalar (0 ,0 ,255) , 3 , CV_AA ); } } else i f ( trackObject < 0 ) paused = f a l s e ; // Apply the ’ negative ’ e f f e c t on the selected region of interest i f ( selectObject && selection . width > 0 && selection . height > 0 ) { Mat roi (image , selection ); bitwise_not ( roi , roi ); } imshow( "CamShift Demo" , image ); imshow( "Histogram " , histimg ); char c = ( char )waitKey (10); i f ( c == 27 ) // esc . break ; switch ( c ) { case ’b ’ : backprojMode = ! backprojMode ; break ; case ’c ’ : trackObject = 0; histimg = Scalar : : a l l (0); break ; case ’h ’ : showHist = ! showHist ; i f ( ! showHist ) destroyWindow( "Histogram" ); else namedWindow( "Histogram " , 1 ); break ; case ’p ’ : paused = ! paused ; break ; default : ; } } } Chapitre 26 Conversion d’un programme C++ en programme Java Script avec emscripten Le langage javascript est reconnu par tous les navigateurs internet et donc les programmes Øcrits en javascript peuvent tourner sur toutes les machines (ou smartphone ou tablettes). Javascript fait partie des trois langages de base pour la programmation des pages web : 1. HTML qui dØ nit le contenu des pages web. Reference. Voici un site qui permet de valider votre code html, ou trouver les erreurs. 2. CSS qui spØci e le style des pages web. Tutorial. Reference. 3. JavaScript qui permet de programmer le comportement des pages web. Tutorial. Reference. Le logiciel emscripten permet de convertir automatiquement du code C++ en code javascript. Cela Øvite de convertir soi mŒme le code la main, ou d’Øcrire en javascript (si on ne connait pas ce langage). De plus le code covertit et compilØ peut Œtre plus rapide que du code javascript natif. Il y a une alternative : Cheerp. Dans cet Section, on explique comment faire cela avec des exemples tr?s simples. Voici des exemples sophistiquØs : Box2d ou Bullets Documentations : Voici des slides. 26.1 Installation Voir Installation avec Portable SDK. On vØri e que l’installation est correcte par ./emcc -v 230 26.2 Conversion C++ -> Javascript Exemple de base : Copier le code suivant dans le chier #include <iostream> using namespace std; double somme(double a,double b) { return a+b; } int main() { double a=2, b=3.1; cout<<" a="<<a<<", b="<<b<<", a+b="<<a+b<<endl; } Compilation : emcc -std=c++11 -O2 -o Cela produit l’executable javascript Execution dans un terminal : node Cela a che : a=2, b=3.1, a+b=5.1 Pour crØer un chier html : emcc -std=c++11 -O2 -o cela crØe un chier et le script part, que l’on execute par : 26.2.1 Autres exemples Voir /emsdk_portable/emscripten/master/tests/ Voir tutorial 26.3 Pour connecter un code C++ et une interface html On dØcrit ici l’utilisation de Cwrap. Autre possibilitØ serait une utilisation de WebIDL (voici un exemple). 26.3.1 Exemple de base RØsultat : Lien : Code C++ : Copier le code suivant dans le chier #include <math.h> extern "C" { //------------int f1(int x) { return x+2; } //------------char* f2(char*s) { char *u; u = new char[3]; u[0]=s[0]; u[1]=s[1]; u[2]=’\0’; // fin de chaine return u; } } Compilation : dans un terminal Øcrire : emcc -std=c++11 -O2 -o -s EXPORTED_FUNCTIONS="[’_f1’,’_f2’]" Cela produit l’executable javascript Remarque 26.3.1. Dans cette commande on rajoute _ au nom de la fonction. Code html : Ecrire le code suivant dans un chier <!DOCTYPE html> <html> <body> <p>Entrer nombre x :</p> <input id="n"> <button type="button" onclick="F()">Submit</button> <h3>resultat f1(x)=x+2:</h3> <p id="zone1"></p> <h3>resultat f2(x): 2 premiers caracteres de x sont:</h3> <p id="zone2"></p> <script src="/"></script> <script> function F() { var x = document.getElementById("n").value; // recupere entree (string) // .. f1 = Module.cwrap(’f1’, ’number’, [’number’]); // declaration de la fonction qui est definie dans var y1 = f1(x); document.getElementById("zone1").innerHTML = y1; // envoie resultat (string) // .. f2 = Module.cwrap(’f2’, ’string’, [’string’]); var y2 = f2(x); document.getElementById("zone2").innerHTML = y2; // envoie resultat (string) } </script> </body> </html> Attention il faut que le script soit prØsent dans le mŒme rØpertoire. Partage du programme : Si vous posez les chiers et dans le mŒme rØpertoire sur un serveur web, alors un utilisateur extØrieur qui charge le chier pourra exØcuter votre programme. Cela marchera depuis toute plateforme (ordinateur sous linux, windows, mac ou mŒme iphone, android etc). Par exemple le programme prØcØdent est utilisable ici : Remarque 26.3.2. Dans le chier , on Øcrit f1 = Module.cwrap(’f1’, ’number’, [’number’,’number’]) pour une fonction avec deux entrØe et une sortie. En sortie : on met null si il n’y a pas de variable. array si on renvoit un tableau d’octets. Il est important de savoir (et parfois utile) que si il y a des variables globales (ex : des tableaux etc) dans le code c++, alors leur contenu est conservØ l’appel suivant. 26.3.2 Exemple avec des Øchanges plus complexes Dans l’exemple prØcØdent, l’interface html et le programme c++ Øchangent seulement une variable de type int ou une chaine de caract?re. Pour Øchanger des variables diverses (provenant de forms et qui en retour peuvent modi er le contenu de la page html), on pourra tout simplement passer l’information codØe dans une unique chaine de caract?res. 26.3.2.1 Exemple Code C++ : ( chier ) #include <math.h> #include <sstream> #include <string> #include <iostream> using namespace std; #include <vector> #include <string.h> //======================================= // Decompose la chaine "| .. | ..|" en sous chaines des caract?res entre les ’|’ =delim vector<string> Decompose(string ligne,char delim) { vector<string> instructions; while((delim)!=string::npos) // il reste une instruction { auto pos= (delim); string ligne2=ligne.substr(0,pos); // .. if(()>0) { // cout<<" I="<<ligne<<flush; instructions.push_back(ligne); } return instructions; } //========================= extern "C" { const char* f(char*s) { //--- parse la chaine en entree: vector<string> vs=Decompose(string(s),’%’); //--- lecture des donnees double x = stod(vs[0]); string m=vs[1]; //--- traitement double y=x+2; string m2="Salut "+m; //----fabrique la chaine de sortie string str_out; str_out = to_string(y) + % + m2; // on choisit le signe % pour separer les donnØes char * s_out = new char [str_out.length()+1]; strcpy(s_out, str_out.c_str()); return s_out; } } //=========================== int main() { // test char s[]="3.14%fred faure"; const char*u = f(s); } Compilation : emcc -std=c++11 -O2 -o -s EXPORTED_FUNCTIONS="[’_f’]" Code html ( chier ) : <!DOCTYPE html> <html> <body> <p>Entrer nombre x :</p> <input type="number" id="x"> <p>Entrer Nom :</p> <input id="m"> <button type="button" onclick="F()">Submit</button> <script src="/"></script> <script> function F() { var x = document.getElementById("x").value; var m = document.getElementById("m").value; var s = x.toString() + "%" + m.toString(); // .. f = Module.cwrap(’f’, ’string’, [’string’]) var u = f(s); document.getElementById("zone2").innerHTML = "debug"; var tu = u.split("%"); // on choisit le signe % pour separer les donnØes document.getElementById("zone1").innerHTML = "x+2=" + tu[0]; document.getElementById("zone2").innerHTML = tu[1]; } </script> <h3>resultat 1</h3> <p id="zone1"></p> <h3>resultat 2</h3> <p id="zone2"></p> </body> </html> Execution : firefox Pour tester le code C++ seulement : Cela utilise la fonction main() de . Compilation : g++ -std=c++11 -O2 -o test Execution : ./test 26.4 Exemples de forms en html Dans l’exemple 26.3.1, on utilisØ une entrØe de type button qui permet de demander une chaine de caract?res l’utilisateur et de la transfØrer au script. Cette entrØe est un cas particulier de form en html. Voici d’autres exemples. RØsultat : Lien : Code html : <!DOCTYPE html> <html> <body> <h2>EntrØes</h2> <br> <fieldset> <legend>Field Set (groupe):</legend> Une entrØe texte (R1): <br> <input id="n"> <br> Boutons radio (R2): <br> <form id="test"> <label><input type="radio" name="test" value="A"> A</label> <label><input type="radio" name="test" value="B" checked> B</label> <label><input type="radio" name="test" value="C"> C</label> </form> </fieldset> <br> Check box (R3): <br> <input type="checkbox" id="cb"> <br> Slider (range) (R4): <br> <input type="range" id="r" name="points" min="0" max="10"> <br> Entree nombre (R5): <br> <input type="number" id="nb" name="quantity" min="1" max="5"> <br> Selection de couleur (R6): <br> <input type="color" id="col" name="favcolor" value="#ff0000"> <br> Choix multiple (R7): <br> <select name="pays" id="pays"> <optgroup label="Europe"> <option value="france">France</option> <option value="espagne">Espagne</option> <option value="italie">Italie</option> <option value="royaume-uni">Royaume-Uni</option> </optgroup> <optgroup label="AmØrique"> <option value="etats-unis">Etats-Unis</option> </optgroup> <optgroup label="Asie"> <option value="chine">Chine</option> <option value="japon">Japon</option> </optgroup> </select> <br> Choix de fichier (R8): <br> <input type="file" id="sel" name="img" multiple> <br> Bouton (action): <br> <button type="button" onclick="F()">Submit</button> <br> <hr> <h2>RØsultats:</h2> <p id="zone"></p> <script> function F() { var x = document.getElementById("n").value; // recupere entree (string) var text1 = "R1 = "+ x; var form = document.getElementById("test"); var text2 = " R2 =" + form.elements["test"].value; var text3 =0; if (document.getElementById("cb").checked) text3=1; text3 = " R3=" + text3; var r4 = document.getElementById("r").value; var r5 = document.getElementById("nb").value; var r6 = document.getElementById("col").value; r6 = " R6=" + r6; var r7 = document.getElementById("pays").value; r7 = " R7=" + r7; var r8 = document.getElementById("sel").value; r8 = " R8=" + r8; document.getElementById("zone").innerHTML = text1 + text2 + text3 + " R4=" + r4 + " R5=" + r5 + r6 + r7 + r8; // envoie resultat (string) sur la zone var m = document.getElementById("m1"); m.setAttribute("value", r4); var m2 = document.getElementById("m2"); m2.setAttribute("value", r4); } </script> <h2>Sorties</h2> Barre de progr?s (progress) (R4): <br> <progress id="m1" value="2" max="10"> </progress> <br> Une jauge (meter) (R4): <br> <meter id="m2" value="2" min="0" max="10"> </meter> <br><hr> </body> </html> 26.5 Appeler des fonctions js depuis c++ et des fonctions c++ depuis js Code c++ : // -*- mode:C++; compile-command: "emcc -std=c++11 -O2 extern "C" { void f() { //.. on passe du code js dans la chaine de caractere ".." emscripten_run_script(" var y=3; document.getElementById(\"zone1\").innerHTML = y;"); // on passe du code js dans (..) EM_ASM( alert(’hello world!’); ); // on passe du code js dans (.. , x0 , x1 ) avec des variables qui sont recuperees par $0,$1 etc double x0=1.2, x1=4.3; EM_ASM_( var x0= $0; var x1 = $1; document.getElementById("zone2").innerHTML = "x0 x1 = " + x0 + x1; , x0, x1); } } Code html : <!DOCTYPE html> <html> <body> <button type="button" onclick="F()">Submit</button> <script src="/"></script> <script> function F() { f = Module.cwrap(’f’, ’string’, [’string’]) f(); } </script> <h3>resultat 1</h3> <p id="zone1"></p> <h3>resultat 2</h3> <p id="zone2"></p> </body> </html> Resultat 26.6 Autres remarques Le prØprocesseur reconnait __EMSCRIPTEN__ qui indique que emcc est le compilateur. 26.7 Graphisme Utiliser la librairie C SDL qui est utilisable en C++ et que emscripten peut exporter. Voir exemples sur /emsdk_portable/emscripten/master/tests/ Utilisation de Root avec Javascript. Utilisatio de Qt avec emscripten. Librairie OpenGL qui est utilisable en C++ et que emscripten peut exporter en WebGL. Voir 26.8 Notes musicales en html5 Voir : Vex ow. ou Alphatab ou Voir , Midi : tutorial Audio : webaudio 26.9 Utilisation des capteurs d’un smartphone Voir capteurs en javascript. 26.10 Utilisation de chiers Supposons que dans le rØpertoire courant il y a un chier contenant abcd . Code c++ : // -*- mode:C++; compile-command: "emcc -std=c++11 -O2 -o -s EXPORTED_FUNCTIONS="[’_f’]" --preload-file " -*- extern "C" { void f() { // lecture depuis fichier ifstream g(""); string res; g>>res; //.. affiche le contenu string s = "document.getElementById(\"zone1\").innerHTML = \"" + res + "\";"; emscripten_run_script(s.c_str()); } } Commande de compilation emcc -std=c++11 -O2 -o -s EXPORTED_FUNCTIONS= --preload-file Code html : <!DOCTYPE html> <html> <body> <button type="button" onclick="F()">Submit</button> <script src="/"></script> <script> function F() { f = Module.cwrap(’f’) f(); } </script> <p id="zone1"></p> </body> </html> Chapitre 27 CrØation automatique de Make le et d’interface graphique utilisateur (GUI) Dans ce chapitre, je prØsente un logiciel que j’ai dØveloppØ pour usage personnel au dØpart. Il Øcrit automatiquement : Un chier Make le pour un projet c++ Du code c++ correspondant des fenŒtres de commandes GUI, utilisant la librairie root. Cela est tr?s utile lorsque on dØveloppe un projet, et permet de gagner du temps. 27.1 Mode d’emploi ce programme sert a creer un chier make le automatiquement. en partant d’un chier "" qui contient le main(). Pour l’utiliser, il faut 1. Øcrire le chier g 2. lancer : makef repertoire nom g <nom_executable> 3. ensuite pour compiler (lancer le Make le), il il faut faire : make clean (pour detruire les chiers .o precedants) make all voir par exemple le script "compp" 27.1.0.1 Fonctionnement nal des commandes Une fenetre de commande GUI est lancØe dans un autre thread (process) "com" et utilise root. Cette fenetre de commande contient tous les widgets des variables (demandees) des classes que utilise le projet main(). Communication : 245 Si un widget est activØ (changØ), le process "com" change les variables du main avec une protection prealable de type mutex. depuis le process "main", on appelle explicitement p_com->Met_a_jour() pour que l’a chage de "com" corresponde aux variables de "main". optionnellement, le process "com" appelle Met_a_jour() de facon periodique (tous les 0.1 sec), pour eviter cet appel explicite. Dans le programme "main", avant/apres d’acceder aux variables partagees, il faut mettre/ enlever le mutex mtx : (); mtx.unlock(); -si besoin, on a acces aux fenetres de commandes par le pointeur Com *p_com; 27.1.0.2 sortie du programme makef chiers c++ : , com.h dans le meme repertoire que chier.h qui sont des classes c++ d’un panneau de commandes 27.1.1 Instructions pour placer les widgets La fenetre de commande peut contenir les zones suivantes : un menu, une zone commune : ZC des zones de tabs : ZT1, ZT2, Les widgets sont placØs la suite horizontalement dans la zone demandee. Retour la ligne : On peut revenir la ligne si on commence par l’instruction "nl" : ex : double x1=4; // make_gui = nl N(ZT("tab1"), "x1=") Texte d’aide : On peut rajouter un texte d’aide (qui sera un Tip ) : ex : double x1=4; // make_gui = N(ZT("tab1"), "x1=") help = texte d’aide N : Numerique entre/sortie pour int, double ex : int a; // make_gui = N(ZC,"a=") double x1=4; // make_gui = N(ZT("tab1"), "x1=") T : Texte entre/sortie pour string ex : string text = "hello"; //make_gui = T(ZT("tab1")) HS : Horizontal Slider entre/sortie pour int/double ex : double x = 1; // make_gui = HS(ZT("tab1"), 0, 5) VS : Vertical Slider : entre/sortie pour int/double PB : Progress Bar : sortie pour int,double ex : double y = 5; // make_gui = PB(ZT("tab2"), 0,5) CHAPITRE 27. CR ATION AUTOMATIQUE DE MAKEFILE ET D’INTERFACE GRAPHIQUE UTILI projet : - H1 : Histogramme 1D pour pour vector<int>, vector<double>, vec les valeurs de ces widgets sont en permanence couplØes la variable c++. Pour cela il y a un pointeur pour chaque classe. ex : pointeur p_C1 pour la classe C1 dans la fonction main(), il faut rajouter (avec la typo) : #includ "com.h" Com *com= new Com(&c1,&c2); //thread t(Lance_com, &c1, &c2); // lance fenetre de commande dans autre thread. Donne pointeurs sur objets On met une typo car cela ne doit pas empecher le fonctionnement de chier.h sans les commandes (on doit pouvoir desactiver en option la compilation de , com.h) et les chiers existants ne doivent pas etre modi Øs. Chapitre 28 A propos des licences Lire licences GPL. Le tØmoignage d’un programmeur : Avec la licence tu donnes legalement le droit aux gens de utiliser le soft; sinon, ils ont juste le droit de le tØlØcharger. Donc il faut toujours une licence. Elle doit Œtre dans l’entŒte de chaque chier. Si le texte de la license est trop long, dans l’entŒte du chier on met juste un paragraphe type indiquant comment obtenir la license; alors celui-ci est mis la racine dans un chier COPYING ou LICENSE. Fais bien attention ne rien altØrer (meme pas les espace et les allØs la ligne), sinon on ne peut plus utiliser des outils (grep et di ) pour savoir quelle est la license; et surtout on se demandera pourquoi le texte a ØtØ altØrØ. Pour que le soft soit libre, en pratique il faut trouver un texte de license existant, connu et ØprouvØ. Sinon l’utilisateur ne sait pas exactement quel droits il a (au sens juridique) et doit payer un avocat si il veut Œtre sur. Donc en pratique c’est comme si il n’y avait pas de license pour le commun des mortels. Pour de l’open-source & libre il y a 2 types de licences : 2. GNU public license (et autres semblables peu connus). Dans ce cas l’auteur demande des choses l’utilisateur, typiquement de ne pas revendre le soft et publier les modifs qu’il y fait, etc. Le texte est en consØquence assez long et ibitable. Mais des juristes s’y sont dØj penchØs et "il est bon". On trouve aussi des license de grands industriels, typiquement 30 pages de charabia de juriste que personne ne comprend; elles sont probablement piegØs. On trouve aussi des licences que des gens ont Øcrit en toute naivetØ et qui n’ont pas de valeur (ie un bon avocat pourrait leur faire dire tout et son contraire). Perso, j’attends rien en retour, donc j’utilise la license ISC pour toute publication. 248 Bibliographie 249 |