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.

M2103-TP6-Exo-1-Corrigé

/**
 *
 * \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,
          
          KExcDivZero  = 11,  // Division par zero

          KExcArg      = 253,
          KExcStd      = 254,
          KExcInconnue = 255 
         };

} // namespace nsUtil

#endif /* __CSTCODERR_H__ */
 
/**
 *
 * @file    IEditable.hpp
 *
 * @authors D. Mathieu, M. Laporte
 *
 * @date    17/03/2010
 *
 * @version V2.0
 *
 **/
#ifndef __IEDITABLE_HPP__
#define __IEDITABLE_HPP__

#include <iostream>

namespace nsUtil
{
    class IEditable;
	/*    */
    std::ostream & operator << (std::ostream & os, 
                                const IEditable & obj);
	/*    */
    class IEditable
    {
      protected :
        virtual std::ostream & display (std::ostream & os) const = 0;
    
      public :
        virtual ~IEditable (void) {}
        friend std::ostream & operator << (std::ostream & os, 
                                           const IEditable & obj);
    }; // IEditable

} // nsUtil

// inline pour remplacer chaque appel par le corps de la fonction
// et donc éviter que ce soit une définition qui entrainerait des risuqes de multiples
// définitions à l'édition de liens.

inline 
std::ostream & nsUtil::operator << (std::ostream & os, 
                                    const IEditable & obj)
{
    return obj.display (os);

} // operator <<

#endif /* __IEDITABLE_HPP__ */
 
/**
 *
 * @file    ClasseEditable.cpp
 *
 * @authors D. Mathieu
 *
 * @date    24/03/2011
 *
 * @version V2.0
 *
 * @brief   Test de la classe IEditable
 *
 **/
#include <iostream>

#include "IEditable.hpp"
#include "CstCodErr.h"

using namespace std;
using namespace nsUtil;    // IEditable

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

    class CA : public IEditable
    {
        virtual ostream & display (ostream & os) const
        {
            return os << "Objet de la classe CA\n";

        } // display()

      public :
        virtual ~CA (void) {}

    }; // CA

    class CB : public IEditable
    {
        virtual ostream & display (ostream & os) const
        {
            return os << "Objet de la classe CB\n";

        } // display()

      public :
        virtual ~CB (void) {}

    }; // CB

    void classeEditable (void)
    {
        CA A;
        CB B;

        contenu (A);
        contenu (B);

    }// classeEditable ()

} // namespace

int main (int argc, char * argv [])
{
    if (argc != 1)
    {
        cerr << "Nombre d'arguments invalide\n"
                "Usage : ./ClasseEditable\n";
        return KExcArg;
    } 

    classeEditable();

    return 0;

} // main()

M2103-TP6-Exo-2

Pour être éditée, la classe CException ne dispose actuellement que de la fonction display(), malcommode.

Il serait préférable de surcharger l’opérateur <<, mais si la classe CException doit ultérieurement être dérivée pour être spécialisée (et elle le sera !), le polymorphisme de l’édition ne fonctionnera pas pour cette nouvelle classe.

C’est pourquoi nous allons en faire une classe utilitaire éditable (c’est-à-dire la faire dériver de
la classe IEditable).

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__  */

Les modifications à apporter à la classe CException sont les suivantes :

  • la dériver publiquement de la classe IEditable (penser à inclure le fichier IEditable.hpp)
  • modifier la méthode publique display() pour qu’elle satisfasse aux nécessités de IEditable.

Le programme ci-dessous est seulement destiné à vérifier que les différents
fichiers ci-dessus sont justes, et que le polymorphisme est correctement implémenté pour
les classes IEditable et CException.

Dans l’espace de noms anonyme du fichier testCException.cpp, dériver la classe
CExcFille de CException, lui ajouter directement dans la déclaration :

  • la fonction display(), qui complète l’édition de sa classe
    mère par un message spécifique (n’importe quoi) ;
  • un constructeur permettant de construire l’objet de la classe mère (passage d’un libellé et d’une constante entière).

Dans la fonction testCException(), lever une exception de la classe CExcFille.

Dans la fonction main(), rétablir le traitement classique des exceptions, en utilisant le nouveau comportement éditable de la classe CException :

int main ()
{
    try 
    { 
        testCException(); 
    }
    catch (const CException & e)
    {
        cerr << e << '\n';
        return e.getCodErr ();
    }
    catch (const exception & e)
    {
        cerr << "Exception standard : " << e.what() << '\n';
        return KExcStd;
    }
    catch (...)
    {
        cerr << "Exception inconnue\n";
        return KExcInconnue;
    }
    return KNoExc;

} // main()

Lui ajouter la vérification que la commande n’a aucun argument, comme dans l’exercice
“Classe abstraite IEditable
Compiler et tester.

Vérifier que la fonction main() affiche bien l’exception levée.

La classe CException est elle aussi devenue un utilitaire.

Pour simplifier les éditions de liens ultérieures, nous vous recommandons de plus d’effectuer les
traitements suivants :

  1. compiler le fichier CException.cpp, par :
    g++ -c CException.cpp -I rep
    

    rep est le répertoire ou se trouve CException.h.

    Noter l’option -I du compilateur g++ qui permet de lui indiquer un chemin où trouver les fichiers de l’utilisateur à inclure s’ils ne sont pas dans le répertoire courant.

  2. ajouter le fichier objet ainsi obtenu dans la bibliothèque libUtil.a :
    ar -cqs libUtil.a CException.o
    

    Vérifier par :

    ar -t libUtil.a
    

Lorsque vous aurez besoin d’utiliser les exceptions (toujours à partir de maintenant) il faudra modifier votre .pro en lui ajoutant la ligne :

LIBS += -L rep -lUtil

rep est le répertoire où se trouve le fichier libUtil.a.

M2103-TP6-Exo-2-Corrigé

 
/**
 *
 * @file    CException.h
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    23/03/2010
 *
 * @version V1.0
 *
 * @brief  classe CException
 *
 **/
#ifndef __CEXCEPTION_H__
#define __CEXCEPTION_H__

#include <string>
#include <iostream>
#include <exception>

#include "CstCodErr.h"
#include "CEditable.h"

namespace nsUtil
{
    class CException : public std::exception, public IEditable
    {
        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;

      protected :
        virtual std::ostream & display (std::ostream & os) const;

    }; // CException
    
} // namespace nsUtil

#endif /* __CEXCEPTION_H__ */
 
/**
 *
 * @file     CException.cpp
 *
 * @authors  M. Laporte, D. Mathieu
 *
 * @date     02/04/2010
 *
 * @version  V1.0
 *
 * @brief    classe CException
 *
 **/
#include <string>
#include <iostream>

#include "CstCodErr.h"
#include "IEditable.hpp"

#include "CException.h"

using namespace std;

ostream & nsUtil::CException::display (ostream & os) const
{ 
    return os << "Exception : " << myLibelle << '\n'
              << "Code      : " << myCodErr;

} // display()


#define CEXC nsUtil::CException

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

const std::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() const noexcept  { return myLibelle.c_str(); }

#undef CEXC

 
/**
 *
 * @file    TestCException.cpp
 *
 * @authors D. Mathieu
 *
 * @date    17/03/2010
 *
 * @version V2.0
 *
 * @brief   Test de la classe CException
 *
 **/
#include <iostream>
#include <string>
#include <esception>

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

using namespace std;
using namespace nsUtil;    // CException

namespace 
{
    class ExcFille : public CException
    {
      public :
        ExcFille (const string & libel, unsigned val) throw ()
          : CException (libel, val) {}

      protected :
        virtual ostream & display (ostream & os) const
        {
            return CException::display (os) << " de la classe fille";

        } // display()

    }; // CFille

    void testCException (void)
    {
        throw ExcFille ("Test du polymorphisme", 25);

    }// testCException ()

} // namespace

int main (int argc, char * argv [])
{
    if (argc != 1)
    {
        cerr << "Nombre d'arguments invalide\n"
                "Usage : ./TestCException\n";
        return KExcArg;
    }
    try
    {
        testCException();

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

} // main()

M2103-TP6-Exo-3

La classe Duree a été partiellement étudiée précédemment.

La dernière version qui en a été faite sert de point de départ à cet exercice.

Créer le projet CDureeEditable.

Y incorporer les fichiers  :

IEditable.hpp

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

#include <iostream>

namespace nsUtil
{
    class IEditable;
    std::ostream & operator << (std::ostream & os, const IEditable & obj);
    
    class IEditable
    {
        virtual std::ostream & display (std::ostream & os) const = 0;
      public :
        virtual ~IEditable (void) {}
        friend std::ostream & operator << (std::ostream & os, const IEditable & obj);

    }; // IEditable
    
} // namespace nsUtil

inline std::ostream & nsUtil::operator << (std::ostream & os, const IEditable & obj)
{
    return obj.display (os);
}

#endif /* __IEDITABLE_HPP__ */

Duree.h

 
/**
 *
 * \file    Duree.h
 *
 * \authors M. Laporte
 *
 * \date    02/04/2018
 *
 * \version V2.0
 *
 * \brief  déclarations de la classe Duree (avec constructeurs et 
 *         destructeur)
 *
 **/
 
 #ifndef __DUREE_H__
 #define __DUREE_H__
 
namespace nsUtil
{
    typedef unsigned long long ULLong_t;

	class Duree
	{
	  private :
		ULLong_t       myDuree;
		short unsigned mySeconds;
		short unsigned myMinutes;
		short unsigned myHours;
		ULLong_t       myDays;

		void normaliser (void);

	  public :
		Duree  (const ULLong_t duree = ULLong_t (0));
		Duree  (const Duree & duree);
		~Duree (void);

		ULLong_t getDuree (void) const;

		void display (void) const;

		void incr (const ULLong_t delta = ULLong_t (0));
		void decr (const ULLong_t delta = ULLong_t (0));
		
		Duree operator +  (const Duree & d) const;
		Duree operator -  (const Duree & d) const;
		
		bool  operator >  (const Duree & d) const;
		bool  operator <  (const Duree & d) const;
		bool  operator != (const Duree & d) const;
		bool  operator == (const Duree & d) const;

	}; // Duree

} // nsUtil

#endif /* __DUREE_H__ */

Duree.cpp

 
/**
 *
 * \file    Duree.cpp
 *
 * \authors M. Laporte
 *
 * \date    02/04/2018
 *
 * \version V2.0
 *
 * \brief  définitions de la classe Duree
 *
 **/
#include <iostream>
#include <iomanip>   // setw()
#include "Duree.h"

using namespace nsUtil;
using namespace std;

#define DUREE nsUtil::Duree

DUREE::Duree  (const ULLong_t duree /* = ULLong_t (0) */) 
    : myDuree (duree) 
{ 
	normaliser ();
	cout << "duree construite : ";
	display ();
    cout << endl;	
	
} // Duree()

DUREE::Duree  (const Duree & duree) 
    : myDuree (duree.getDuree ()) 
{ 
	normaliser ();
	cout << "duree construite par recopie : ";
	display ();
    cout << endl;	
	
} // Duree()

DUREE::~Duree  (void) 
{ 
	cout << "duree détruite : ";
	display ();
    cout << endl;	
	
} // Duree()

void DUREE::normaliser (void)
{
	myDays    =  myDuree / 86400;
	myHours   = (myDuree % 86400) / 3600;
	myMinutes  = (myDuree % 3600) / 60;
	mySeconds =  myDuree % 60;
	
} // normaliser()

ULLong_t DUREE::getDuree (void) const { return myDuree; }

void DUREE::display (void) const
{
	cout << '[' 
	     << setw (10) << myDays    << " jour (s) " 
	     << setfill ('0')
		 << setw (2)  << myHours   << " heure(s) "
		 << setw (2)  << myMinutes << " minute(s) "
		 << setw (2)  << mySeconds << " seconde(s) "
		 << setfill (' ')
		 << ']';
		 
} // display()

void DUREE::incr (const ULLong_t delta /* = ULLong_t (0) */)
{
	myDuree += delta;
	normaliser ();
	
} // incr()

void DUREE::decr (const ULLong_t delta /* = ULLong_t (0) */)
{
	myDuree -= (delta > myDuree) ? myDuree : delta;
	
} // decr()

DUREE DUREE::operator + (const Duree & d) const
{
	return myDuree + d.myDuree;
	
} // operator +()

DUREE DUREE::operator - (const Duree & d) const
{
	return myDuree - (myDuree < d.myDuree ? myDuree : d.myDuree);
	
} // operator -()

bool DUREE::operator > (const Duree & d) const
{
	return myDuree > d.myDuree;
	
} // operator >()

bool DUREE::operator < (const Duree & d) const
{
	return myDuree < d.myDuree;
	
} // operator <()

bool DUREE::operator != (const Duree & d) const
{
	return myDuree != d.myDuree;
	
} // operator !=()

bool DUREE::operator == (const Duree & d) const
{
	return myDuree == d.myDuree;
	
} // operator ==()

#undef DUREE

De nombreuses améliorations vont lui être apportées.

Copiez aussi le programme de test

testDuree.cpp


/**
 *
 * @file    TestDuree.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    17/03/2010
 *
 * @version V1.0
 *
 * @brief   Test de la classe CDuree complete
 *
 **/
#include <cassert>
#include <sstream>
#include <exception>
#include <iostream>

#include "CException.h"    // de l'exercice précédent
#include "CstCodErr.h"
#include "Duree.h"

using namespace std;
using namespace rel_ops;
using namespace nsUtil;    // CException

namespace
{
    void testDuree_01 (void)
    {
        DureeEditable d1 (3661);
        DureeEditable d2 (2661);
        DureeEditable d3 (3661);

        assert (! (d1 < d2));
        assert (  (d2 < d1));

        assert (   d1 > d2);
        assert (! (d2 > d1));

        assert (! (d1 <= d2));
        assert (   d2 <= d1);

        assert (   d1 >= d2);
        assert (! (d2 >= d1));

        assert (! (d1 == d2));
        assert (! (d2 == d1));
        assert (   d1 == d3);

        assert (   d1 != d2);
        assert (   d2 != d1);

    } // testDuree_01()

    void testDuree_02 (void)
    {
        DureeEditable d1 (3661);
        {
            ostringstream oss;
            oss << d1;
            assert (oss.str() == "[     0:01:01:01]");
        }
        {
            ostringstream oss;
            oss << d1--;
            assert (oss.str() == "[     0:01:01:01]");
        }
        {
            ostringstream oss;
            oss << d1--;
            assert (oss.str() == "[     0:01:01:00]");
        }
        {
            ostringstream oss;
            oss << --d1;
            assert (oss.str() == "[     0:01:00:58]");
        }
        {
            ostringstream oss;
            oss << d1;
            assert (oss.str() == "[     0:01:00:58]");
        }
        {
            ostringstream oss;
            oss << d1++;
            assert (oss.str() == "[     0:01:00:58]");
        }
        {
            ostringstream oss;
            oss << d1++;
            assert (oss.str() == "[     0:01:00:59]");
        }
        {
            ostringstream oss;
            oss << ++d1;
            assert (oss.str() == "[     0:01:01:01]");
        }
        {
            ostringstream oss;
            oss << ++d1;
            assert (oss.str() == "[     0:01:01:02]");
        }
        {
            ostringstream oss;
            oss << ++d1;
            assert (oss.str() == "[     0:01:01:03]");
        }
        {
            ostringstream oss;
            oss << (d1 -= 3);
            assert (oss.str() == "[     0:01:01:00]");
        }
        {
            ostringstream oss;
            oss << (d1 += 3);
            assert (oss.str() == "[     0:01:01:03]");
        }
        {
            ostringstream oss;
            oss << (d1 += Duree (3));
            assert (oss.str() == "[     0:01:01:06]");
        }
        {
            d1.setDuree (0);
            ostringstream oss;
            oss << d1;
            assert (oss.str() == "[     0:00:00:00]");
        }

    } // testDuree_02)

    void testDuree_03 (void)
    {
        DureeEditable d1;
        bool isCaught = false;

        try { d1--; }
        catch (const CException & e) { isCaught = true; }
        assert (isCaught);

        isCaught = false;
        try { --d1; }
        catch (const CException & e) { isCaught = true; }
        assert (isCaught);

        isCaught = false;
        try { d1 -= 1; }
        catch (const CException & e) { isCaught = true; }
        assert (isCaught);

        isCaught = false;
        try { d1 -= Duree (3); }
        catch (const CException & e) { isCaught = true; }
        assert (isCaught);

    } // testDuree_03()

} // namespace anonyme

int main (int argc, char * argv [])
{
    if (argc != 1)
    {
        cerr << "Nombre d'arguments invalide\n"
                "Usage : TestCDuree\n";
        return KErrArg;
    }
    try
    {
        testDuree_01 ();
        testDuree_02 ();
        testDuree_03 ();
        cout << "Fin normale\n";

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

} // main()

Modifications à effectuer dans la classe Duree :

  • ajouter le qualifieur explicit devant le constructeur par défaut (cela évite les conversions implicites
    de n’importe quel entier en Duree, que pourrait faire indûment le compilateur),
  • remplacer les fonctions incr() et decr() par les opérateurs de pre/post-in/dé crémentation,
    CX & operator ++ (void) noexcept; // préincrémentation de la classe CX
    CX   operator ++ (int)  noexcept; // postincrémentation de la classe CX
    
    
  • ajouter les deux surcharges de chacun des opérateurs d’affectation += et -= ayant pour paramètre soit un entier unsigned long long, soit un objet Duree (profile),
    Duree & operator += (const Duree &) noexcept;
    
  • ajouter les deux opérateurs + et - ayant pour paramètre un entier
    unsigned long long,
  • ajouter le modifieur setDuree() ayant pour paramètre un unsigned long long avec une valeur
    par défaut,
  • les opérateurs -, -- et -= sont susceptibles de lever une
    CException de code d’erreur KExcOpInterdite de valeur 201.

Un objet ne doit pas savoir comment il doit être affiché (nous avons fait une exception pour la classe
CException qui a pour vocation d’être affiché sur un flux standard de sortie).
La fonction display() n’a donc rien à faire dans la classe Duree.

Dans l’espace de nom anonyme du fichier TestDuree.cpp écrire la classe DureeEditable.

    • La dériver publiquement des classes Duree et IEditable (penser à inclure les
      fichiers IEditable.hpp et Duree.h).
    • Supprimer la fonction-membre publique display() de la classe Duree et ajouter la fonction-membre protégée display(), de même profil que la fonction abstraite display() de IEditable à la classe DureeEditable.

Modifier la fonction main() du fichier TestDuree.cpp, pour lui conserver la structure qui a été utilisée dans l’exercice précédent.

Compiler et tester.

M2103-TP6-Exo-3-Corrigé

 
/**
 *
 * @file    Duree.h
 *
 * @authors M. Laporte, d. Mathieu
 *
 * @date    07/03/2008
 *
 * @version V1.0
 *
 * @brief   Declarations de la classe duree
 *
 **/
#ifndef __DUREE_H__
#define __DUREE_H__

typedef unsigned long long ULLong_t;

#include "IEditable.hpp"
#include "CException.hpp"

namespace nsUtil
{
    class Duree 
    {
      private :
        ULLong_t       myDuree;
        short unsigned mySecondes;
        short unsigned myMinutes;
        short unsigned myHeures;
        ULLong_t       myJours;
    
        void normaliser (void)                                noexcept;
    

      public :
        explicit Duree  (const ULLong_t duree = ULLong_t (0)) noexcept;

        ULLong_t getDuree (void) const                        noexcept;
        void setDuree (const ULLong_t duree = ULLong_t (0))   noexcept;

        Duree & operator ++ (void)                            noexcept;
        Duree   operator ++ (int)                             noexcept;
        Duree & operator -- (void)                  throw (CException);
        Duree   operator -- (int)                   throw (CException);

        Duree operator + (ULLong_t duree) const               noexcept;
        Duree operator - (ULLong_t duree) const     throw (CException);

        Duree & operator += (ULLong_t d)                      noexcept;
        Duree & operator -= (ULLong_t d)            throw (CException);

        Duree operator + (const Duree & d) const             noexcept;
        Duree operator - (const Duree & d) const   throw (CException);

        Duree & operator += (const Duree & d)                noexcept;
        Duree & operator -= (const Duree & d)      throw (CException);

        // Operateurs de comparaison et d'identite

        bool operator <  (const Duree & d) const              noexcept;
        bool operator == (const Duree & d) const              noexcept;

    }; // Duree
    
} // namespace nsUtil

#endif /* __DUREE_H__ */
 
/**
 *
 * @file    Duree.cpp
 *
 * @authors M. Laporte, d. Mathieu
 *
 * @date    17/03/2010
 *
 * @version V1.0
 *
 * @brief   Definition des methodes de la classe Duree
 *
 **/

#include "CException.hpp"

#include "Duree.h"
#include "CstCodErr.h"

#define CDUREE nsUtil::Duree

using namespace std;
using namespace nsUtil;

CDUREE::Duree  (const ULLong_t duree /* = UULong_t (0) */) noexcept
    : myDuree (duree) { normaliser (); }

ULLong_t CDUREE::getDuree (void) const noexcept { return myDuree; }

void CDUREE::setDuree (ULLong_t duree) noexcept 
{
    myDuree = duree;
    normaliser ();

}  // setDuree()


void CDUREE::normaliser (void) noexcept
{
      mySecondes = myDuree % 60;
      myMinutes  = myDuree / 60 % 60;
      myHeures   = myDuree / 3600 % 24;
      myJours    = myDuree / 86400;

} // normaliser()

CDUREE CDUREE::operator - (ULLong_t d) const throw (CException)
{
    if (myDuree < d)
        throw CException ("- : operation interdite",
                          KExcOpInterdite);

    return Duree (myDuree - d);

} // operator -

CDUREE::Duree CDUREE::operator + (ULLong_t d) const noexcept
{
    return Duree (myDuree + d);

} // operator +

CDUREE CDUREE::operator - (const Duree & d) const throw (CException)
{
    if (myDuree < d.myDuree)
        throw CException ("- : operation interdite",
                          KExcOpInterdite);

    return Duree (myDuree - d.myDuree);

} // operator -

CDUREE::Duree CDUREE::operator + (const Duree & d) const noexcept
{
    return Duree (myDuree + d.myDuree);

} // operator +

CDUREE & CDUREE::operator ++ (void) noexcept
{
    ++myDuree;
    normaliser ();
    return *this;

} // operator ++  pre

CDUREE CDUREE::operator ++ (int) noexcept 
{
    return Duree (myDuree++); 

} // operator ++ post

CDUREE & CDUREE::operator -- (void) throw (CException)
{
    if (myDuree == 0)
        throw CException ("-- : operation interdite",
                          KExcOpInterdite);
    --myDuree;
    normaliser ();
    return *this;

} // operator -- pre

CDUREE CDUREE::operator -- (int) throw (CException)
{
    if (myDuree == 0)
        throw CException ("-- : operation interdite",
                          KExcOpInterdite);

    return Duree (myDuree--);

} // operator -- post

CDUREE & CDUREE::operator += (ULLong_t d) noexcept
{
    myDuree += d;
    normaliser ();

    return *this;

} // operator +=

CDUREE & CDUREE::operator -= (ULLong_t d) throw (CException)
{
    if (myDuree < d)
        throw CException ("-= : operation interdite",
                          KExcOpInterdite);
    myDuree -= d;
    normaliser ();

    return *this;

} // operator -=

CDUREE & CDUREE::operator += (const Duree & d) noexcept
{
    myDuree += d.myDuree;
    normaliser ();

    return *this;

} // operator +=

CDUREE & CDUREE::operator -= (const Duree & d) throw (CException)
{
    if (myDuree < d.myDuree)
        throw CException ("-= : operation interdite",
                          KExcOpInterdite);
    myDuree -= d.myDuree;
    normaliser ();

    return *this;

} // operator -=

// Les valeurs par defaut sont rappelees en commentaires car
//   interdites

bool CDUREE::operator < (const Duree & d) const noexcept
{
    return (myDuree < d.myDuree);

} // operator <

bool CDUREE::operator == (const Duree & d) const noexcept
{
    return (myDuree == d.myDuree);

} // operator ==

#undef CDUREE

 
/**
 *
 * @file    testDuree.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    07/12/2011
 *
 * @version V1.0
 * 
 * @brief   test de la classe Duree complete
 *
 **/
#include <iostream>
#include <exception>
#include <iomanip>   // boolalpha

#include "CException.hpp"
#include "CstCodErr.h"
#include "Duree.hpp"
#include "IEditable.hpp"

using namespace std;
using namespace rel_ops;
using namespace nsUtil;    // CException

#define classdef typedef

namespace 
{
namespace
{
    class DureeEditable : public Duree, public IEditable
    {
      protected :
        virtual std::ostream & display (std::ostream & os) const;
      public :

        explicit DureeEditable (const ULLong_t duree = ULLong_t (0))
                                                           noexcept;
        // constructeur nécessaire pour utiliser les opérateurs
        // qui renvoient
        // des Duree (un DureeEditable est un Duree, mais pas
        // l'inverse)
        DureeEditable (const Duree & duree)               noexcept;

        virtual ~DureeEditable (void)                     noexcept;

        /*     */
        DureeEditable & operator ++ (void)                            noexcept;
        DureeEditable & operator -- (void)                  throw (CException);
        DureeEditable   operator ++ (int)                             noexcept;
        DureeEditable   operator -- (int)                   throw (CException);
        DureeEditable & operator += (ULLong_t d)                      noexcept;
        DureeEditable & operator -= (ULLong_t d)            throw (CException);
        DureeEditable & operator += (const DureeEditable & d)                noexcept;
        DureeEditable & operator -= (const DureeEditable & d)      throw (CException);
        /*     */

    }; // DureeEditable

    DureeEditable::DureeEditable (const ULLong_t duree
                                           /* = ULLong_t (0) */) noexcept
        : Duree (duree) {}

    DureeEditable::DureeEditable (const Duree & duree) noexcept
        : Duree (duree) {}

    DureeEditable::~DureeEditable (void) noexcept {}

    ostream & DureeEditable::display (ostream & os) const
    {
        return
             os << '['                           << setfill (' ')
                << setw (6) << myJours    << ':' << setfill ('0')
                << setw (2) << myHeures   << ':'
                << setw (2) << myMinutes  << ':'
                << setw (2) << mySecondes << ']' << setfill (' ');

    } // display()
/*     */
    DureeEditable & DureeEditable::operator ++ (void) noexcept
    {
        return *this = Duree::operator ++ ();

    } // operator ++()

    DureeEditable & DureeEditable::operator -- (void) throw (CException)
    {
        return *this = Duree::operator -- ();

    } // operator --()

    DureeEditable DureeEditable::operator ++ (int) noexcept
    {
        return *this = Duree::operator ++ (0);

    } // operator ++()

    DureeEditable DureeEditable::operator -- (int) throw (CException)
    {
        return *this = Duree::operator -- (0);

    } // operator --()

    DureeEditable & DureeEditable::operator += (ULLong_t d) noexcept
    {
        return *this = Duree::operator += (d);

    } // operator +=()

    DureeEditable & DureeEditable::operator -= (ULLong_t d) throw (CException)
    {
        return *this = Duree::operator -= (d);

    } // operator -=()

    DureeEditable & DureeEditable::operator += (const DureeEditable & d) noexcept
    {
        return *this = Duree::operator += (d);

    } // operator +=()

    DureeEditable & DureeEditable::operator -= (const DureeEditable & d) throw (CException)
    {
        return *this = Duree::operator -= (d);

    } // operator -=()

    void testDuree_01 (void)
    {
        DureeEditable d1 (3661);
        DureeEditable d2 (2661);
        DureeEditable d3 (3661);

        cout << boolalpha;

        cout << d1 << " <  " << d2 << " : " << (d1 <  d2) << '\n';
        cout << d2 << " <  " << d1 << " : " << (d2 <  d1) << "\n\n";

        cout << d1 << " >  " << d2 << " : " << (d1 >  d2) << '\n';
        cout << d2 << " >  " << d1 << " : " << (d2 >  d1) << "\n\n";

        cout << d1 << " <= " << d2 << " : " << (d1 <= d2) << '\n';
        cout << d2 << " <= " << d1 << " : " << (d2 <= d1) << "\n\n";

        cout << d1 << " >= " << d2 << " : " << (d1 >= d2) << '\n';
        cout << d2 << " >= " << d1 << " : " << (d2 >= d1) << "\n\n";

        cout << d1 << " == " << d2 << " : " << (d1 == d2) << '\n';
        cout << d2 << " == " << d1 << " : " << (d2 == d1) << "\n\n";
        cout << d1 << " == " << d3 << " : " << (d1 == d3) << "\n\n";

        cout << d1 << " != " << d2 << " : " << (d1 != d2) << '\n';
        cout << d1 << " != " << d3 << " : " << (d1 != d3) << "\n\n";

        cout << noboolalpha;

    } // testDuree_01()

    void testDuree_02 (void)
    {
        DureeEditable d1 (3661);
        cout << "d1   = " << d1   << '\n';
        cout << "d1-- = " << d1-- << '\n';
        cout << "d1-- = " << d1-- << '\n';
        cout << "--d1 = " << --d1 << '\n';
        cout << "--d1 = " << --d1 << "\n\n";

        cout << "d1   = " << d1   << '\n';
        cout << "d1++ = " << d1++ << '\n';
        cout << "d1++ = " << d1++ << '\n';
        cout << "++d1 = " << ++d1 << '\n';
        cout << "++d1 = " << ++d1 << "\n\n";

        cout << "d1   = " << d1   << '\n';
        cout << "d1 -= 3 " << (d1 -= 3) << '\n';
        cout << "d1 += 3 " << (d1 += 3) << "\n\n";
        cout << "d1 += Duree (3) " << (d1 += Duree (3)) << "\n\n";

        d1.setDuree (0);
        cout << "d1   = " << d1   << '\n';

    } // testDuree_03()

    void testDuree_03 (void)
    {
        DureeEditable d1;
        try { d1--; }
        catch (const CException & e)
        {
            cout << "d1-- :\n" << e << '\n';
        }

        try { --d1; }
        catch (const CException & e)
        {
            cout << "--d1 :\n" << e << '\n';
        }

        try { d1 -= 1; }
        catch (const CException & e)
        {
            cout << "d1 -= 1 :\n" << e << '\n';
        }

        try { d1 -= Duree (3); }
        catch (const CException & e)
        {
            cout << "d1 -= Duree (3):\n" << e << '\n';
        }

    } // testDuree_03()

} // namespace

int main (int argc, char * argv [])
{
    if (argc != 1)
    {
        cerr << "Nombre d'arguments invalide\n"
                "Usage : testDuree\n";
        return CstErrArg;
    }
    try
    {
        /*        */

        cout << "\ntest 1\n";
        testDuree_01 ();

        /*        */
        /*        */

        cout << "\n\ntest 2\n";
        testDuree_02 ();

        /*        */
        /*        */

        cout << "\n\ntest 3\n";
        testDuree_03 ();

        /*        */

        return CstNoExc;
    }
    catch (const CException & e)
    {
        cerr << e << '\n';
        return e.getCodErr();
    }
    catch (const exception & e)
    {
        cerr << "Exception standard : " << e.what() << '\n';
        return CstExcStd;
    }
    catch (...)
    {
        cerr << "Exception inconnue\n";
        return CstExcInconnue;
    }

} // main()