Aller au contenu

Programmation C++ : TP séance 01

Rendu du TP

Pour rendre votre travail :

  • Créez un répertoire progcpp-TPxx-NOM1-NOM2, en replaçant xx par le numéro de la séance, NOM1 par votre nom de famille, NOM2 par le nom de famille de votre binôme ou mono si vous êtes seul(e). Il ne doit y avoir aucun espace ou apostrophe dans le nom du répertoire.

  • Recopiez tous vos fichiers sources dans ce répertoire, ainsi que le Makefile éventuel, mais pas les fichiers compilés !

    Dans chaque fichier il doit y avoir au début en commentaire : vos NOMS Prénoms ainsi que ceux de votre binôme éventuel.

  • Compressez le répertoire sous la forme d'une archive au format tgz puis téléversez l'archive sur la page Ametice, même si vous n'avez pas fini la planche de TP.

Dans le cas où le TP n'est achevé, il vous est demandé de le terminer chez vous, puis de re-téléverser l'archive dans sa version finale, sans changer son nom, avant le début de la séance suivante.

Compresser le répertoire

Pour compresser le répertoire, vous pouvez faire clic droit / compresser dans le gestionnaire de fichier, ou taper dans le terminal :

(cd .. && tar cvfz progcpp-TPxx-NOM1-NOM2.tgz --ignore-failed-read \
                   progcpp-TPxx-NOM1-NOM2/{Makefile,*.cpp,*.hpp} && ls)

Vous pouvez également recopier cette fonction dans le terminal, qui utilise directement le nom du répertoire courant comme nom de fichier :

mysvg() (
    local p=$(pwd)      # le chemin absolu
    local d=${p##*/}    # le répertoire courant
    echo "Sauvegarde de '$d/' dans '$d.tgz' ..."
    cd .. || return 1
    tar cvfz "$d.tgz" --ignore-failed-read \
        "$d"/{Makefile,*.cpp,*.hpp} 2>/dev/null 
    ls -ls "$d.tgz"
)

puis l'utiliser en tapant :

mysvg

Exercice 1. Dessins de dominos en SVG, entrées-sorties, arguments et paramètres

1.a. Dessin en SVG

Voici un exemple de dessin en SVG :

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300" height="200">
  <title>Exemple simple de figure SVG</title>
  <desc>
    Cette figure est constituée d'un rectangle,
    d'un segment de droite et d'un cercle.
  </desc>

  <line x1="5" y1="5" x2="250" y2="95" stroke="red" />
  <circle cx="110" cy="80" r="50" fill="blue" />
  <rect width="100" height="80" x="10" y="80" rx="10" ry="10"
        style="fill:rgb(200,200,200);stroke-width:2;stroke:black" />
  <text x="180" y="60">Un texte</text>
</svg>

Recopier ce texte dans un fichier avec l'extension .svg puis vérifier qu'on peut l'ouvrir avec un navigateur, gimp ou inkscape (si installé).

Écrire un programme en C++ domino1.cpp qui vérifie les arguments de la ligne de commande1. Si l'argument --exemple-svg est présent, il génère un fichier dont le nom est l'argument suivant, et écrit ligne à ligne dans le fichier, à l'aide de l'opérateur <<, le texte de l'exemple de dessin SVG ci-dessus2. Si le second argument est manquant il y a un message d'erreur et le programme échoue en renvoyant 1.

Exemple de sortie :

$ ./domino1 
Usage: ./domino1 --help pour afficher l'aide
$ ./domino1 --help
Options :
  --help                   affiche l'aide
  --exemple-svg fichier    écrit un exemple en SVG
$ ./domino1 --exemple-svg
Erreur: nom de fichier svg attendu
$ ./domino1 --exemple-svg image.svg
Génération de image.svg ...
$ inkscape image.svg &

Compléments de cours :

Tester les arguments

Cet exemple montre comment tester les arguments de la ligne de commande avec le module cstring :

#include <iostream> 
#include <cstring> 

int main (int argc, char *argv[]) 
{
    if (argc == 1) {
        std::cout << "Usage: " << argv[0] << " --help pour afficher l'aide\n";
    } 
    else if (strcmp(argv[1], "--help") == 0) {
        std::cout << "Options :\n"
                  << "  --help      affiche l'aide\n";
    }
}
Écrire dans un fichier

Cet exemple montre comment écrire dans un fichier :

#include <iostream>
#include <fstream>

int main() {
    // Crée et ouvre un fichier en écriture
    std::ofstream f {"exemple.txt"};

    // Écrit dans le fichier
    f << "Ceci est \"du texte\" sur\n"
      << "plusieurs lignes.\n";

    // Ferme le fichier
    f.close();
} 

1.b. Dessin d'un domino

Les dominos sont au nombre de 28, et sont numérotés pour n allant de 0 à 6 : n|0, ..., n|n.

Un domino dont les points sont a|b est dessiné par un rectangle formé de 2 carrés séparés par un trait, au centre duquel il y a un petit cercle plein (le rivet) ; dans les carrés il y a un nombre a, respectivement b, de cercles bien disposés.

Rajouter dans le programme une fonction qui génère un fichier SVG, dans lequel est dessiné un domino orienté verticalement, en fonction des paramètres a et b. Elle sera appelée par le programme principal pour les arguments --domino a b image.svg, avec les tests sur le nombre d'arguments.

Rappel : la fonction atoi() permet de convertir un char* en int.

Taille des lignes de code

Pensez à limiter la taille de vos lignes de code à (environ) 80 caractères, en alignant lorsque nécessaire (par exemple des tests à rallonge) de manière à en faciliter la relecture.

1.c. Dessin des 28 dominos

Déclarer des constantes1 entières globales :

  • IMAGE_WIDTH et IMAGE_HEIGHT seront la taille en pixel de l'image SVG générée ;
  • DOMINO_SIDE sera la largeur en pixels du petit côté d'un domino, qui aura donc comme longueur 2*DOMINO_SIDE ;

définir également des constantes pour les couleurs et épaisseurs des différents éléments d'un domino.

Écrire une fonction write_preamble_svg qui reçoit par référence1 un fichier f déjà ouvert en écriture, puis écrit l'entête SVG nécessaire dans f pour créer une image de taille IMAGE_WIDTH et IMAGE_HEIGHT.

Écrire une fonction write_postamble_svg qui reçoit par référence un fichier f déjà ouvert en écriture, puis écrit la fin du fichier SVG.

Écrire une fonction write_domino_svg qui reçoit les paramètres suivants, puis dessine le domino dans le fichier :

  • un fichier f déjà ouvert, par référence ;
  • les nombres de points a et b du domino ;
  • l'orientation horizontale ou verticale (un booléen) ;
  • les coordonnées cx, cy du centre du domino dans l'image, en pixels.

Écrire une fonction draw_all_dominos_svg qui reçoit en paramètre le nom d'un fichier SVG, l'ouvre, écrit l'entête du fichier SVG, dessine chacun des 28 dominos dans l'image en les positionnant verticalement en 7 colonnes sur 4 lignes, écrit la fin du fichier puis ferme le fichier.

Rajouter dans le main un argument de la ligne de commande --all-dominos image.svg qui déclenchera l'appel de la fonction draw_all_dominos_svg.

Compléments de cours :

Déclaration de constantes

Pour définir une constante il suffit de précéder la déclaration par const, par exemple :

const int foo = 1;
Passage par référence

Le passage par référence d'un paramètre est indiqué à l'aide de l'opérateur & ; le paramètre devient alors un alias de la variable passée en paramètre. Exemple :

void foo (int& bar)             // passage par référence
{
    bar += 1;
}

int thud = 5;
foo(thud);
std::cout << thud << "\n";      // affiche : 6

1.d. Fichier de positions

Écrire un fichier texte de positions de dominos, où chaque ligne représente un domino avec la syntaxe suivante :

a b is_vertical grid_x grid_y

a et b représentent le nombre de points du domino, is_vertical vaut 1 si le domino est vertical, 0 sinon, enfin grid_x et grid_y sont les coordonnées du centre du domino sur une grille de largeur et hauteur DOMINO_SIDE/2.

Pourquoi ces coordonnées sur une grille ? Selon les règles de placement des dominos, un domino double n|n est obligatoirement placé perpendiculairement à un autre domino *|n. On considère donc cette grille où chaque domino occupe 2 par 4 cases, et le centre de chaque domino est situé sur un sommet de la grille.

Écrire une fonction read_positions_file qui reçoit en paramètre le nom d'un fichier de positions, et par référence un tableau de struct1 de taille indéterminée, dans lequel seront stockées les informations lues.

La fonction lit le fichier1 et stocke les informations lues pour chaque domino dans chaque case du tableau. Elle renvoie le nombre de positions lues.

Écrire une fonction write_dominos_from_positions_svg qui reçoit en paramètres le nom d'un fichier de positions et le nom d'un fichier SVG. Elle déclare un tableau de struct de taille 28 ; elle lit le fichier de positions, ouvre le fichier SVG, écrit l'entête, puis dessine dans le fichier SVG chacun des dominos issus du fichier de positions, enfin écrit la fin du fichier SVG puis le ferme.

Rajouter dans le main un argument de la ligne de commande --from-positions positions.txt image.svg qui déclenchera l'appel de la fonction write_dominos_from_positions_svg.

Compléments de cours :

Déclaration d'un struct

Cet exemple montre comment déclarer un struct :

struct MyStruct {
    int foo;
    double bar;
};

Utilisation :

MyStruct shme;
shme.foo += shme.bar;
Lire dans un fichier

Cet exemple montre comment lire dans un fichier constitué de lignes contenant chacune 2 entiers :

#include <iostream>
#include <fstream>

int main() {
    // Ouvre un fichier en lecture
    std::ifstream f {"exemple.txt"};

    // Lectures dans le fichier de 2 entiers par itération
    int a, b;
    while (f >> a >> b) {
        std::cout << "Lu : " << a << " et " << b << "\n";
    }

    // Ferme le fichier
    f.close();
} 

  1. Voir complément de cours à la fin de la question 

  2. Penser à protéger les " par des \