M2103-TP7-Exo-1-Corrigé

/**
 *
 * @file    TypesBase.hpp
 *
 * @authors D. Mathieu, M. Laporte
 *
 * @date    26/04/2010
 *
 * @version V1.0
 *
 * @brief   Encapsulation de quelques types de base
 *
 **/
#pragma once

#include <cstdint>    // int32_t, int16_t

namespace std
{
    template <typename T>
    class TypeBase
    {
        T myVal;

      public :
        explicit TypeBase (T val = T ()) noexcept : myVal (val) {}
        operator T & () noexcept { return myVal; }
        operator const T & () const noexcept { return myVal; }
  
    }; // CTypeBase

typedef TypeBase <int32_t> Integer;
typedef TypeBase <int16_t> Short;
typedef TypeBase <uint8_t> Character;

} // namespace std

M2103-TP7-Exo-2

Le passage d’une fonction (ou plutôt d’un pointeur de fonction) en paramètre d’une autre fonction est une technique très ancienne, puisqu’elle date de l’apparition du langage C.

Il est indispensable de la connaître pour cette raison (c’est la seule possibilité de “généricité” de traitement en C), et elle est encore utilisée par certains langages comme Ada95.

Cependant, le C++ offre un moyen beaucoup plus élégant, et surtout plus performant : les functors (voir l’amphi correspondant).

Au lieu de passer une fonction f1() en paramètre d’une autre fonction f2(), il suffit de créer une classe contenant cette fonction f1() comme méthode (fonction-membre, opération), d’instancier cette classe en un objet obj, de passer cet objet à la fonction f2(), qui appellera la méthode f1() de son paramètre : param.f1 (...).

class Obj
{
    // ...
  public :
    ... f1 (...) 
    { 
        ... 

    } // f1

    // ...

}; // Obj

void f2 (Obj & param)
{
    // ...
    ... param.f1 (...);
    // ...

} // F2()

Le dernier obstacle est l’obligation qu’a la fonction f2() de connaître l’identificateur de la fonction f1().

Il est levé en surchargeant l’opérateur () dans la classe et en donnant à cet opérateur le même profil et le même corps que la fonction f1().

Un objet de la classe ainsi modifiée est appelé un “objet-fonction” ou plus simplement un functor

Ainsi, la fonction f2() se contente d’appeler param (...) au lieu de param.f1 (...).

Travail à effectuer

Créer le projet Functor.

remplacer le contenu du fichier main.cpp par celui du fichier TestFunctor.cpp

/**
 *
 * @file   TestFunctor.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    27/04/2010
 *
 * @version V1.0
 *
 * @brief   Premier functor 
 *
 **/
#include <iostream>
#include <cassert>
#include <cctype>  // tolower(), toupper()
#include <string>

using namespace std;

namespace 
{
    class ToLower
    {
        // ToDo

    }; // ToLower

    typedef int (* fctInt2Int_t) (int);
    string & moulinette (string & str, fctInt2Int_t transf)
    {
        for (string::size_type i (str.size ()); i--; )
            str [i] = transf (str [i]);

        return str;

    } // moulinette()

    void testFunctor (void)
    {
        cout << "Functor : ";

        string ligne  ("AZECv qrgWSg wrV  wfdgWFDHG  wdfGWXCV");
        string minusc ("azecv qrgwsg wrv  wfdgwfdhg  wdfgwxcv");

        // ToDo 
        assert (minusc == moulinette (ligne, ...));

        cout << "OK\n";

    } // testFunctor()

} // namespace anonyme

int main (void)
{
    /*      */    testFunctor ();     /*           */

    return 0;

} // main()

Complétez la classe ToLower possédant :

  • un destructeur virtuel,
  • une surcharge (virtuelle) de l’opérateur (), ayant le même profil que la fonction C tolower() :
    int tolower (int); 
    

    qui se contente d’appeler cette dernière et d’en renvoyer le résultat.

Modifiez la fonction moulinette() en remplaçant le second paramètre par un functor de la classe ToLower, et complétez l’appel de la fonction assert() dans la fonction testFunctor().

Remarque : la lourdeur de mise en œuvre est à peu près du même ordre que celle de l’utilisation des pointeurs de fonction, et, pour le moment, la fonction moulinette() ne peut faire qu’une seule opération : mettre en
minuscules.

Nous verrons ultérieurement les avantages décisifs des functors.

M2103-TP7-Exo-2-Corrigés

 
/**
 *
 * @file   TestFunctor.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    07/12/2011
 *
 * @version V1.0
 *
 * @brief   Premier functor 
 *
 **/
#include <iostream>
#include <string>
#include <cctype>  // tolower(), toupper()
#include <cassert>

using namespace std;

namespace 
{
    class ToLower
    {
      public :
        virtual ~ToLower (void) {}
        virtual int operator () (int caract) const
        {
            return tolower (caract);

        } // operateur()

    }; // ToLower

    string & moulinette (string & str, const ToLower & transf)
    {
        for (string::size_type i (str.size ()); i--; )
            str [i] = transf (str [i]);

        return str;

    } // moulinette()

    void testFunctor (void)
    {
        cout << "Functor : ";

        string ligne  ("AZECv qrgWSg wrV  wfdgWFDHG  wdfGWXCV");
        string minusc ("azecv qrgwsg wrv  wfdgwfdhg  wdfgwxcv");

        assert (minusc == moulinette (ligne, ToLower()));

        cout << "OK\n";

    } // testFunctor()

} // namespace

int main (void)
{
    /*      */    testFunctor();     /*           */

    return 0;

} // main()

M2103-TP7-Exo-3

Dans l’exercice précédent, nous avons signalé que nous avons perdu en généralité par rapport à la technique de passage de pointeur de fonction, puisque la fonction moulinette() est limitée à un seul type de traitement.

Nous allons maintenant lui rendre sa généralité.

Pour mettre la chaîne en majuscules par exemple, il faut commencer par écrire la nouvelle classe ToUpper, et y surcharger l’opérateur () adéquat.

Pour que la fonction moulinette() puisse mettre la chaîne en majuscules ou en minuscules (ou effectuer toute autre transformation), il faut utiliser le polymorphisme.

Travail à effectuer

Créer le projet FunctorAbstrait.

Dans votre fichier main.cpp copier le fichier TestFunctorAbstrait.cpp

/**
 *
 * @file    TestFunctorAbstrait.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    27/04/2010
 *
 * @version V1.0
 *
 * @brief   functor abstrait
 *
 **/
#include <string>
#include <iostream>
#include <cctype>  // ispunct(), islower(), isalpha()
                   // tolower(), toupper()
#include <cassert>

using namespace std;

namespace 
{
    class ToLower
    {
      public :
        virtual ~ToLower (void) {}
        virtual int operator () (int caract) const
        {
            return tolower (caract);

        } // operateur()

    }; // CToLower

    string & moulinette (string & str, const ToLower & transf)
    {
        for (string::size_type i (str.size ()); i--; )
            str [i] = transf (str [i]);

        return str;

    } // moulinette()

    void testFunctor (void)
    {
        cout << "Functor abstrait : ";

        string ligne     ("azert:;,.?GFDSQ");
        string minusc    ("azert:;,.?gfdsq");
        string majusc    ("AZERT:;,.?GFDSQ");
        string sansPunct ("AZERT     GFDSQ");

        // ToDo

        assert (minusc    == moulinette (ligne, ...));
        assert (majusc    == moulinette (ligne, ...));
        assert (sansPunct == moulinette (ligne, ...));

        cout << "OK\n";

    } // testFunctor ()

} // namespace anonyme

int main (void)
{
    /*      */    testFunctor ();     /*           */

    return 0;

} // main()

Dans l’espace de noms anonyme, ajoutez-lui la classe abstraite ITraitCar dont l’unique fonction-membre abstraite a le même profil que tolower(), toupper(), etc.

N’oubliez pas son destructeur virtuel !

Faites dériver publiquement la classe ToLower de ITraitCar.

Sur le même modèle, écrivez les nouvelles classes ToUpper et IgnPunct qui transforment respectivement les minuscules en majuscules et les caractères de ponctuation en espaces.

Modifiez le type du second paramètre de moulinette() en conséquence.

Le tour est joué !

Dans la fonction testFunctor (), complétez les instructions assert().

Compilez et testez.

M2103-TP7-Exo-3-Corrigés

 
/**
 *
 * @file    TestFunctorAbstrait.cpp
 *
 * @authors M. Laporte
 *
 * @date    07/05/2018
 *
 * @version V1.0
 *
 * @brief   functor abstrait
 *
 **/
#include <string>
#include <iostream>
#include <cctype>     // ispunct(), islower(), isalpha()
                      // tolower(), toupper()
#include <cassert>

using namespace std;

namespace 
{
    class ITraitCar
    {
      public :
        virtual ~ITraitCar (void) {}
        virtual int operator () (int caract) const = 0;

    }; // ITraitCar

    class ToLower  : public ITraitCar
    {
      public :
        virtual ~ToLower (void) {}
        virtual int operator () (int caract) const noexcept
        {
            return tolower (caract);

        } // operateur()

    }; // ToLower

    class ToUpper : public ITraitCar
    {
      public :
        virtual ~ToUpper (void) {}
        virtual int operator () (int caract) const noexcept
        {
            return toupper (caract);

        } // operateur()

    }; // ToUpper

    class IgnPunct : public ITraitCar
    {
      public :
        virtual ~IgnPunct (void) {}
        virtual int operator () (int caract) const noexcept
        {
            return ispunct (caract) ? ' ' : caract;

        } // operateur()

    }; // IgnPunct

    string & moulinette (string & str, const ITraitCar & transf)
    {
        for (string::size_type i (str.size()); i--; )
            str [i] = transf (str [i]);

        return str;

    } // moulinette()

    void testFunctor (void)
    {
        cout << "Functor abstrait : ";

        string ligne     ("azert:;,.?GFDSQ");
        string minusc    ("azert:;,.?gfdsq");
        string majusc    ("AZERT:;,.?GFDSQ");
        string sansPunct ("AZERT     GFDSQ");

        assert (minusc    == moulinette (Ligne, ToLower ()));
        assert (majusc    == moulinette (Ligne, ToUpper ()));
        assert (sansPunct == moulinette (Ligne, IgnPunct()));

        cout << "OK\n";

    } // testFunctor()

} // namespace

int main (void)
{
    /*      */    testFunctor ();     /*           */

    return 0;

} // main()

M2103-TP7-Exo-4

L’utilisation d’un functor à la place d’un pointeur de fonction, comme nous venons de le voir, offre une très importante possibilité : il peut contenir des données-membres susceptibles d’être modifiées, et qui peuvent changer dynamiquement le comportement de l’opérateur ().

De même, si le functor est une donnée-résultat, certaines de ces données-membres peuvent être récupérées après l’appel de moulinette().

C’est ce que nous allons faire ici.

Travail à effectuer

Créer le projet FunctorInOut.

Remplacer le contenu de votre fichier main.cpp par le contenu du fichier TestFunctorAbstraitCorr.cpp (corrigé de l’exercice précédent) :

/**
 *
 * @file    TestFunctorAbstraitCorr.cpp
 *
 * @authors M. Laporte
 *
 * @date    07/05/2018
 *
 * @version V1.0
 *
 * @brief   functor abstrait
 *
 **/
#include <string>
#include <iostream>
#include <cctype>    // ispunct(), islower(), isalpha()
                     // tolower(), toupper()
#include <cassert>

using namespace std;

namespace 
{
    class ITraitCar
    {
      public :
        virtual ~ITraitCar (void) {}
        virtual int operator () (int caract) const = 0;

    }; // ITraitCar

    class ToLower  : public ITraitCar
    {
      public :
        virtual ~ToLower (void) {}
        virtual int operator () (int caract) const noexcept
        {
            return tolower (caract);

        } // operateur()

    }; // ToLower

    class ToUpper : public ITraitCar
    {
      public :
        virtual ~ToUpper (void) {}
        virtual int operator () (int caract) const noexcept
        {
            return toupper (caract);

        } // operateur()

    }; // ToUpper

    class IgnPunct : public ITraitCar
    {
      public :
        virtual ~IgnPunct (void) {}
        virtual int operator () (int caract) const noexcept
        {
            return ispunct (caract) ? ' ' : caract;

        } // operateur()

    }; // IgnPunct

    string & moulinette (string & str, const ITraitCar & transf)
    {
        for (string::size_type i (str.size()); i--; )
            str [i] = transf (str [i]);

        return str;

    } // moulinette()

    void testFunctor (void)
    {
        cout << "Functor abstrait : ";

        string ligne     ("azert:;,.?GFDSQ");
        string minusc    ("azert:;,.?gfdsq");
        string majusc    ("AZERT:;,.?GFDSQ");
        string sansPunct ("AZERT     GFDSQ");

        assert (minusc    == moulinette (ligne, ToLower ()));
        assert (majusc    == moulinette (ligne, ToUpper ()));
        assert (sansPunct == moulinette (ligne, IgnPunct()));

        cout << "OK\n";

    } // testFunctor()

} // namespace

int main (void)
{
    /*      */    testFunctor ();     /*           */

    return 0;

} // main()

Dans la classe ITraitCar, ajoutez :

  • la donnée-membre myCpt, qui est un compteur modifiable par l’opérateur () (on ne sait pas encore comment, puisque la fonction est abstraite !),
  • un constructeur qui initialise à 0 la donnée-membre myCpt,
  • l’accesseur et le modifieur correspondants.

Dans la classe ToUpper, modifiez l’opérateur () pour qu’il cumule le nombre de fois qu’il a été appelé.

Dans la classe ToLower, modifiez l’opérateur () pour qu’il cumule le nombre de fois qu’il a été appelé sur une lettre (minuscule ou majuscule).

Dans la classe IgnPunct, modifiez l’opérateur () pour qu’il cumule le nombre de caractères remplacés.

Dans la fonction testFunctor(), vérifiez au moyen d’un assert() le nombre de transformations effectuées après chaque appel à moulinette().

Remarque

la réalisation exacte de l’énoncé ci-dessus conduit à une erreur de compilation.

En effet, l’opérateur () modifie le contenu de la donnée-membre myCpt, donc il ne devrait pas être suivi de const.

De même, le functor passé en paramètre à la fonction moulinette() est une donnée-résultat, puisqu’on en récupère le contenu après traitement.

Deux solutions peuvent être envisagées :

  1. transformer l’opérateur () en accesseur/modifieur (suppression de const de son profil) et transformer le paramètre formel functor de moulinette() en donnée-résultat (supprimer son qualifieur const),
  2. qualifier la donnée membre myCpt de la classe mère “mutable“, car l’opérateur () est déclaré const et pourtant,
    il modifie l’objet …
    Cette solution correspond à l’idée que ces comptages sont faits “à l’insu” de l’utilisateur, par exemple dans une optique de mise au point ou de profiling de programme (étude du comportement d’un programme, statistiques d’utilisation, etc… en vue de son optimistion).

C’est cette dernière solution que nous présentons dans le corrigé.

M2103-TP7-Exo-4-Corrigé

 
/**
 *
 * @file    TestFunctorInOut.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    07/12/2011
 *
 * @version V1.0
 *
 * @brief   functor abstrait
 *
 **/
#include <string>
#include <iostream>
#include <cctype>   // ispunct(), islower(), isalpha()
                    // tolower(), toupper()
#include <cassert>

using namespace std;

namespace 
{
    class ITraitCar
    {
      protected :
        mutable unsigned myCpt;

      public :
        ITraitCar (void) : myCpt (0) {}

        unsigned getCpt (void)   const noexcept { return myCpt;  }
        void     setCpt (unsigned cpt) noexcept { myCpt = cpt;   }

        virtual ~ITraitCar (void) {}
        virtual int operator () (int caract) const = 0;

    }; // ITraitCar

    class CToUpper : public ITraitCar
    {
      public :
        virtual ~CToUpper (void) {}
        virtual int operator () (int caract) const
        {
            ++myCpt;

            return toupper (caract);

        } // operateur()

    }; // CToUpper

    class CIgnPunct : public ITraitCar
    {
      public :
        virtual ~CIgnPunct (void) {}
        virtual int operator () (int caract) const
        {
            return ispunct (caract) ? ++myCpt, ' ' : caract;

        } // operateur()

    }; // CIgnPunct

    class CToLower : public ITraitCar
    {
      public :
        virtual ~CToLower (void) {}
        virtual int operator () (int caract) const
        {
            if (isalpha (caract)) ++myCpt;
            return tolower (caract);

        } // operateur()

    }; // CToLower

    string & moulinette (string & str, const ITraitCar & transf)
    {
        for (string::size_type i (str.size ()); i--; )
            str [i] = transf (str [i]);

        return str;

    } // moulinette()

    void testFunctor (void)
    {
        cout << "FunctorInOut : ";

        string ligne     ("Phrase ... Avec,,:,; pOnCtUaTiOn");
        string minusc    ("phrase ... avec,,:,; ponctuation");
        string majusc    ("PHRASE ... AVEC,,:,; PONCTUATION");
        string sansPunct ("PHRASE     AVEC      PONCTUATION");

        ToLower  toLower;
        assert (minusc    == moulinette (ligne, toLower ));
        assert (21 == toLower.getCpt());

        ToUpper  toUpper;
        assert (majusc    == moulinette (ligne, toUpper ));
        assert (32 == toUpper.getCpt());

        IgnPunct ignPunct;
        assert (sansPunct == moulinette (ligne, ignPunct));
        assert ( 8 == ignPunct.getCpt());

        cout << "OK\n";

    } // testFunctor()

} // namespace

int main (void)
{
    /*      */    testFunctor ();     /*           */

    return 0;

} // main()

M2103-TP7-Exo-5

Les functors sont très largement utilisés dans la bibliothèque standard du C++, au point qu’il est illusoire de penser pouvoir se servir de cette bibliothèque si on n’a pas compris les functors.

Nous en montrerons quelques exemples dans des exercices ultérieurs.

Dans l’exercice présent, nous allons généraliser “encore plus” des algorithmes “généraux” que nous avons déjà écrits, comme par exemple des fonctions de tri, et ceci grâce aux functors.

Considérons le code suivant, inspiré de l’exercice Tri par sélection du TP sur les tris:

template <typename T, typename Iter_t>
void selectSort (Iter_t deb, Iter_t fin, 
                 bool (* isInf) (const T &, const T &))
{
    if (fin <= deb) return;

    for ( ; deb < fin - 1; ++deb)
    {
        Iter_t rgMin = deb;
        for (Iter_t j (deb + 1); j < fin; ++j) 
            if (isInf (*j, *rgMin)) rgMin = j;
        swap (*deb, *rgMin);
    }

} // selectSort()

Il s’agit de remplacer dans cette fonction le passage de la fonction isInf() par un functor.

Dans l’exercice précédent, le functor ITraitCar utilisé était abstrait (la fonction de comparaison était inconnue), mais il restait très spécifique : il ne pouvait comparer que des caractères.

Dans cet exercice, nous allons nous affranchir de cette limitation en rendant le functor générique

Travail à effectuer

Créer le projet LessThanAbstrGen.

Dans l’espace de noms anonyme du fichier LessThanAbstrGen.cpp, recopier la classe suivante :

    class Pers /* : public IEditable */
    {
        string   myNom;
        unsigned myAge;

      public :
        Pers (const string & nom, unsigned age)
              : myNom (nom), myAge (age) {}

        const string & getNom (void) const noexcept { return myNom; }
        unsigned       getAge (void) const noexcept { return myAge; }

      private :
        ostream & display (ostream & os)  const
        {
            return os << getAge () << " - " << getNom ();

        } // display()

      public :    // ajouté pour faciliter les essais
	  
        friend ostream & operator << (ostream & os, const Pers & p)
        {
            return p.display (os);
        }

    }; // Pers

Ecrire les deux classes TriParAgeAsc et TriParNomDesc, qui implémentent chacun l’opérateur () – les identificateurs sont suffisamment explicites – dont les deux paramètres sont des objets de la classe Pers et qui renvoie un booléen (ce qui correspond au profil d’un opérateur de comparaison).

La fonction selectSort() ne peut pas directement accepter comme dernier paramètre un functor d’une ou de l’autre classe, qui sont différentes.

Il est donc nécessaire de créer une classe plus générale qui puisse “représenter” n’importe lequel de ces deux functors, ou tout autre comparateur à venir.

Pour cela, ajouter la classe abstraite générique de functors ILessThanGen, classe d’interface qui ne contient que le profil de l’opérateur (), et en faire dériver publiquement les deux classes TriParAgeAsc et TriParNomDesc.

Dans la fonction selectSort() dont le code vous est donné, remplacer le second paramètre de généricité qui représente le type des valeurs à trier par le type du functor à utiliser pour la comparaison.

La seule contrainte (la spécification) est que ce paramètre “supporte” l’opérateur ().

Utiliser le code suivant pour tester vos functors :

    void testLessThanAbstrGen (void)
    {
        cout << "LessThanAbstrGen : \n";

        typedef vector <Pers> CVPers;
        typedef CVPers::size_type IndPers_t;
        CVPers vPers;

        vPers.push_back (Pers ("Charlotte", 21));
        vPers.push_back (Pers ("Alfred",    12));
        vPers.push_back (Pers ("Jean",      42));
        vPers.push_back (Pers ("Noemie",    11));
        vPers.push_back (Pers ("Berthe",    99));
        vPers.push_back (Pers ("Agathe",    29));
        vPers.push_back (Pers ("Sylvain",   42));
        vPers.push_back (Pers ("Pierre",    75));

        cout << "\nTri par age croissant\n\n";

        selectSort (vPers.begin(), vPers.end(), TriParAgeAsc());

        for (const Pers & personne : vPers)
            cout << personne << '\n';

        cout << "\nTri par nom decroissant\n\n";

        selectSort (vPers.begin (), vPers.end (), TriParNomDesc ());

        for (const Pers & personne : vPers)
            cout << personne << '\n';

    } // testLessThanAbstrGen()

Remarques

  1. Il n’est pas nécessaire d’instancier explicitement les deux comparateurs avant de les passer en paramètre de la fonction
    selectSort(), un simple appel aux constructeurs suffit (parce que le paramètre formel est une donnée :
    const).
  2. Comme on peut le constater, la fonction selectSort() peut maintenant trier n’importe quel type de données
    (généricité sur le type T) selon n’importe quel critère de tri (relation d’ordre).

    Il s’agit d’une nouvelle “jolie” utilisation du polymorphisme !

    Et en plus, on pourrait éventuellement récupérer des résultats intermédiaires du traitement, en mettant en œuvre la technique vue dans l’exercice précédent, qui consiste à charger le functor de données-membres !

M2103-TP7-Exo-5-Corrigés

 
/**
 *
 * @file    LessThanAbstrGen.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    07/12/2011
 *
 * @version V1.0
 *
 **/
#include <string>
#include <vector>
#include <iostream>

using namespace std;

namespace 
{
    template <typename T>
    class ILessThanGen
    {
      public :
        virtual ~ILessThanGen (void) {}
        virtual bool operator () (const T &, const T &) const = 0;

    }; // ILessThanGen

    class Pers
    {
        string   myNom;
        unsigned myAge;

      public :
        Pers (const string & nom, unsigned age)
            : myNom (nom), myAge (age) {}

        const string & getNom (void) const noexcept { return myNom; }
        unsigned       getAge (void) const noexcept { return myAge; }

      private :
        ostream & display (ostream & os)  const
        {
            return os << getAge() << " - " << getNom();

        } // display()

      public :
        friend ostream & operator << (ostream & os, const Pers & p)
        {
            return p.display (os);

        }

    }; // Pers

    class TriParAgeAsc : public ILessThanGen <Pers>
    {
      public :
        virtual ~TriParAgeAsc (void) noexcept {}
        
        virtual bool operator () (const Pers & p1, const Pers & p2)
                        const noexcept
        {
            return p1.getAge () <= p2.getAge ();

        } // operator ()

    }; // TriParAgeAsc

    class TriParNomDesc : public ILessThanGen <Pers>
    {
      public :
        virtual ~TriParNomDesc (void) noexcept {}
        
        virtual bool operator () (const Pers & p1, const Pers & p2)
                        const noexcept
        {
            return p1.getNom () >= p2.getNom ();

        } // operator ()

    }; // TriParNomDesc

    template <typename Iter_t, class LessThan>
    void selectSort (Iter_t deb, Iter_t fin, const LessThan & isInf)
    {
        if (fin <= deb) return;

        for ( ; deb < fin - 1; ++deb)
        {
            Iter_t rgMin = deb;
            for (Iter_t j (deb + 1); j < fin; ++j) 
                if (isInf (*j, *rgMin)) rgMin = j;
            swap (*deb, *rgMin);
        }

    } // selectSort()

    void lessThanAbstrGen (void)
    {
        cout << "LessThanAbstrGen : \n";

        typedef vector <Pers> CVPers;

        CVPers vPers;

        vPers.push_back ( Pers ("Charlotte", 21));
        vPers.push_back ( Pers ("Alfred",    12));
        vPers.push_back ( Pers ("Jean",      42));
        vPers.push_back ( Pers ("Noemie",    11));
        vPers.push_back ( Pers ("Berthe",    99));
        vPers.push_back ( Pers ("Agathe",    29));
        vPers.push_back ( Pers ("Sylvain",   42));
        vPers.push_back ( Pers ("Pierre",    75));

        cout << "Tri par age croissant\n\n";

        selectSort (vPers.begin (), vPers.end (), TriParAgeAsc ());

        for (const Pers & personne : vPers)
            cout << personne << '\n';

        cout << "Tri par nom decroissant\n\n";

        selectSort (vPers.begin (), vPers.end (), TriParNomDesc ());

        for (const Pers & personne : vPers)
            cout << personne << '\n';

    } // lessThanAbstrGen()

} // namespace

int main (void)
{
    lessThanAbstrGen ();

    return 0;

} // main()

M2103-TP6-Exo-1

Avant de commencer, il est conseillé de réviser l’amphi consacré aux classes abstraites.

Nous avons montré, en cours, l’utilité d’écrire la classe abstraite IEditable, afin de
permettre à toute classe qui en est dérivée de disposer de l’opérateur d’injection <<
qui mette en œuvre le polymorphisme.

Cela oblige tout utilisateur à écrire, pour chacune de ses propres classes, une fonction display() privée ou protégée.

Travail demandé

Créer le projet ClasseEditable.

Y copier le contenu des fichiers CException.h ,

/**
 *
 * \file     CException.h
 *
 * \authors  M. Laporte, D. Mathieu
 *
 * \date     10/02/2011
 *
 * \version  V1.0
 *
 * \brief    Declaration de la classe CException
 *
 **/
#ifndef __CEXCEPTION_H__
#define __CEXCEPTION_H__

#include <string>
#include <exception>

#include "CstCodErr.h"

namespace nsUtil
{
    class CException : public std::exception
    {
        std::string myLibelle;
        unsigned    myCodErr;

      public :
        CException (const std::string & libelle = std::string(), 
                    const unsigned      codErr  = KNoExc)     noexcept;
        virtual ~CException (void)                            noexcept;

        const std::string & getLibelle (void) const           noexcept;
        unsigned            getCodErr  (void) const           noexcept;

        virtual const char* what       (void) const           noexcept;

        void display (void) const;

    }; // CException
    
} // namespace nsUtil

#endif /*  __CEXCEPTION_H__  */

CException.cpp

/**
 *
 * \file     CException.cpp
 *
 * \authors  M. Laporte, D. Mathieu
 *
 * \date     10/02/2011
 *
 * \version  V1.0
 *
 * \brief    classe CException
 *
 **/
#include <string>
#include <iostream>

#include "CstCodErr.h"
#include "CException.h"

using namespace std;

#define CEXC nsUtil::CException

//==========================
// Classe nsUtil::CException
//==========================

CEXC::CException (const string & libelle /* = string () */,
                  const unsigned codErr  /* = KNoExc  */) noexcept 
    : myLibelle (libelle), myCodErr (codErr) {}

const string & CEXC::getLibelle (void) const noexcept 
{
    return myLibelle;

} // GetLibelle()

unsigned CEXC::getCodErr (void) const noexcept { return myCodErr;  }

CEXC::~CException (void) noexcept {}

const char* CEXC::what (void) const noexcept  { return myLibelle.c_str(); }

void CEXC::display (void) const
{ 
    cout << "Exception : " << myLibelle << '\n'
         << "Code      : " << myCodErr  << endl;

} // Afficher()

#undef CEXC

et CstCodErr.h

/**
 *
 * \file     CstCodErr.h
 *
 * \authors  M. Laporte, D. Mathieu
 *
 * \date     10/02/2011
 *
 * \version  V1.0
 *
 * \brief    Codes d'erreurs
 *
 **/
#ifndef __CSTCODERR_H__
#define __CSTCODERR_H__

namespace nsUtil
{
    enum {KNoExc       = 0,
          KNoError     = 0,

          KExcStd      = 254,
          KExcInconnue = 255 
         };

} // namespace nsUtil

#endif /*  __CSTCODERR_H__  */

Dans le fichier IEditable.hpp, écrire la classe abstraite IEditable de l’espace de noms nsUtil, conformément aux indications de l’amphi 4 de POO. Nous n’y avions pas défini l’opérateur ami <<, voici sa définition :

ostream & operator << (ostream & os, const IEditable & editable)
{
    editable.display (os);
    return os;

} // operator <<()

Afin de tester le fonctionnement de cette classe, recopier dans l’espace de noms anonyme du fichier
ClasseEditable.cpp, la fonction suivante :

void contenu (const IEditable & ed) { cout << ed << endl; }

Ajouter deux classes CA et CB, dérivées de IEditable, qui ne se distinguent que par leur fonction virtuelle display() (elles affichent leur identificateur).

Dans la fonction testClasseEditable() de l’espace de noms anonyme du fichier
ClasseEditable.cpp, instancier les classes CA et CB, et éditer les deux objets au moyen de la fonction contenu().

Garder à la fonction main() la structure qui lui a été donnée dans le TP précédent :

fichier ClasseEditable.cpp :

/**
 *
 * \file   : ClasseEditable.cpp
 *
 * \author : 
 *
 * \date   : 
 *
**/

#include <iostream>
#include <exception>

#include "CstCodErr.h"
#include "CException.h"

using namespace std;
using namespace nsUtil;

namespace
{
    void testXXX (void) ...

} // namespace

int main (int argc, char * argv [])
{
    try
    {
        testXXX ();

        return KNoExc;
    }
    catch (const CException & e)
    {
        cerr << "Erreur        : " << e.getLibelle () << '\n' 
             << "Code d'erreur = " << e.getCodErr ()  << '\n';
        return e.getCodErr();
    }    
    catch (const exception & e)
    {
        cerr << "Exception standard : " << e.what () << '\n'; 
        return KExcStd;
    }    
    catch (...)
    {
        cerr << "Exception inconnue\n";
        return KExcInconnue;
    }    

} // main()

Vérifier qu’aucun argument n’est passé à la commande.

Compiler et tester.

La classe IEditable est devenue un utilitaire.