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()

M2103 – TP5-Corrigés

Fichier Alarme.h


/**
 *
 * \file    Alarme.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Alarme
 *
 **/
#ifndef _ALARME
#define _ALARME

namespace nsDigicode {

	class Alarme {
	  public:
		void declencher (void);
		
	}; // Alarme
	
} /* nsDigicode */

#endif /* _ALARME */

Fichier Alarme.cpp


/**
 *
 * \file    Alarme.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Alarme
 *
 **/
#include <iostream>
#include <unistd.h>   // sleep()
using namespace std;
#include "Alarme.h"

void nsDigicode::Alarme::declencher (void) {
	while (true) {
		cout << "Alarme : la porte n'est pas fermée" << endl;
		sleep(1);
	} 
} // declencher()

Fichier Archivage.h


/**
 *
 * \file    Archivage.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Archivage
 *
 **/
#ifndef _ARCHIVAGE
#define _ARCHIVAGE

namespace nsDigicode {

	class Archivage {
	  public:
		void archiver (int);
		
	}; // Archivage
	
} /* ns_Digicode */

#endif /* _ARCHIVAGE */

Fichier Archivage.cpp


/**
 *
 * \file    Archivage.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Archivage
 *
 **/
#include <fstream>
#include <ctime>     // time_t, time ()

using namespace std; #include "Archivage.h" #define NOM_FIC "../DigicodeIUT/Archive.txt" #define MODE std::fstream::out | std::fstream::app void nsDigicode::Archivage::archiver (int code) { ofstream leFichier (NOM_FIC,MODE); time_t dateActuelle; time (&dateActuelle); leFichier << "Code : " << code << " Date : " << ctime (&dateActuelle) << endl; leFichier.close (); } //archiver()

Fichier BD.h


/**
 *
 * \file    BD.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe BD
 *
 **/
#ifndef _BD
#define _BD

namespace nsDigicode {

	class BD {
	  public:
		bool verifier (int);
		
	}; // BD
	
} /* ns_Digicode */

#endif /* _BD */

Fichier BD.cpp


/**
 *
 * \file    BD.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe BD
 *
 **/
#include <iostream>           
#include <fstream>           
using namespace std;
#include "BD.h"

#define NOM_FIC "../DigicodeIUT/Base.txt"

bool nsDigicode::BD::verifier (int code) {
int lu = -1; cout << "Vérification du code" << endl; ifstream leFichier; leFichier.open (NOM_FIC); while (!leFichier.eof () && lu != code ) leFichier >> lu; leFichier.close (); if (lu != code) cout << "Code erroné" << endl; return lu == code; } //verifier()

Fichier Capteur.h


/**
 *
 * \file    Capteur.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Capteur
 *
 **/
#ifndef _CAPTEUR
#define _CAPTEUR

namespace nsDigicode {

	class Capteur {
	  public:
		int detecter (void);
		
	}; // Capteur
	
} /* ns_Digicode */

#endif /* _CAPTEUR */

Fichier Capteur.cpp


/**
 *
 * \file    Capteur.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Capteur
 *
 **/
#include "Capteur.h"                  
#include "kbhit.h"


int nsDigicode::Capteur::detecter (void) { return  _kbhit(); }   

Fichier Chrono.h


/**
 *
 * \file    Chrono.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Chrono
 *
 **/
#ifndef _CHRONO
#define _CHRONO

namespace nsDigicode {

	class ObjetTempo;
	
	class Chrono 	{
		bool actif;
		static void* compter (void *);
	  public:
		void demarrer (ObjetTempo*);
		void arreter (void);
		Chrono (void);
		
	}; // Chrono
	
} /* ns_Digicode */

#endif /* _CHRONO */

Fichier Chrono.cpp


/**
 *
 * \file    Chrono.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Chrono
 *
 **/
#include 
#include 

using namespace std;

#include 
#include "ObjetTempo.h"
#include "Chrono.h"         

void nsDigicode::Chrono::arreter (void) { actif = false; } 

nsDigicode::Chrono::Chrono (void) { actif = false; }

void* nsDigicode::Chrono::compter (void * p) { 
	double duree   = (clock_t) ( (ObjetTempo *) p)->getDelai ();
	Chrono *lec    = ( (ObjetTempo *) p)->getChrono ();
	clock_t debut  = clock ();
	double tempsEcoule = (clock () - debut) / CLOCKS_PER_SEC;
	lec->actif = true;
	while (lec->actif && tempsEcoule<duree) {="" tempsecoule="(clock" ()="" -="" debut)="" clocks_per_sec;="" }="" if="" (tempsecoule="">= duree ) {
		 ( (ObjetTempo *) p)->finTempo ();
		lec->actif = false;
	}
	pthread_exit (NULL);
	
} //compter()

void nsDigicode::Chrono::demarrer (ObjetTempo * a) {  
	pthread_t thread;
	while (actif) std::cout.flush ();
//	double duree = a->getDelai ();
	std::cout.flush ();
	pthread_create (&thread, NULL, compter, (void *) a);
	
} //demarrer()
</duree)>

Fichier Clavier.h


/**
 *
 * \file    Clavier.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Clavier
 *
 **/
#ifndef _CLAVIER
#define _CLAVIER

namespace nsDigicode {

	class Clavier {
	  public:
		int saisieChiffre (void);
		
	}; // Clavier
	
} /* ns_Digicode */

#endif /* _CLAVIER */

Fichier Clavier.cpp


/**
 *
 * \file    Clavier.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Clavier
 *
 **/
#include 

#include "Clavier.h"                  
#include "kbhit.h"
using namespace std;

int nsDigicode::Clavier::saisieChiffre (void) {
	int c ;
	if (c = _kbhit()) {
		cout << "*" << flush;
		return c - '0';
	}	
	return -1;
	
} // saisieChiffre()  

Fichier Interface.h


/**
 *
 * \file    Interface.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Interface
 *
 **/
#ifndef _INTERFACE
#define _INTERFACE
#include 

#include "ObjetTempo.h"
#include "Clavier.h"
#include "Voyant.h"
#include "Chrono.h"

namespace nsDigicode {

	class Interface : public ObjetTempo {
		int     nbChiffresAttendus;
		Clavier leClavier;
		Voyant  leVoyantVert;
		Voyant  leVoyantRouge;
		
		void statut (std::string = "\n");
		
	public:
		int saisieCode (void);
		Interface (Chrono*, int, int);
		
	}; // Interface
	
} /* ns_Digicode */

#endif /* _INTERFACE */

Fichier Interface.cpp


/**
 *
 * \file    Interface.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Interface
 *
 **/
#include 
#include 
using namespace std;

#include "Interface.h"                  
#include "Chrono.h"

nsDigicode::Interface::Interface (Chrono * lech, int d, int nbca) : 
	ObjetTempo (lech, d),
	leVoyantRouge (rouge, eteint),
	leVoyantVert (vert, eteint),
	nbChiffresAttendus (nbca)
{}  

void nsDigicode::Interface::statut (string s) {
	cout << endl     << "VERT"  << setw(4) << leVoyantVert.getStatut() 
		 << setw(7)  << "ROUGE" << setw(4) << leVoyantRouge.getStatut() 
		 << setw(24) << s << flush;
		 
} // statut()

int nsDigicode::Interface::saisieCode (void) { 
    int code, chiffreLu, nbChiffresLus;
    attente = false;
    do {
	leVoyantVert.eteindre ();
	leVoyantRouge.allumer ();
	statut ("Entrez votre code : "); 
	cin.clear ();
	code = 0;
	while ((code = leClavier.saisieChiffre ()) == -1);
	nbChiffresLus = 1;
	leVoyantVert.allumer ();
	statut ("Continuez la saisie : ");
	attente = true;
	leChrono->demarrer (this);
	while (attente && nbChiffresLus < nbchiffresattendus) {
while (attente && (chiffrelu = leClavier.saisieChiffre())
== -1);
if (attente) {
code = code * 10 + chiffrelu;
nbchiffreslus++;
if (nbchiffreslus == nbchiffresattendus)
lechrono->arreter ();
} else cout << endl << "Temps ecoulé - Fin de saisie" << endl; } while (nbChiffresLus < nbChiffresAttendus ); leVoyantRouge.eteindre (); statut (); return code; } // saisieCode() </nbchiffresattendus)>

Fichier kbhit.h


/**
 *
 * \file    kbhit.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la fonction _kbhit()
 *
 **/
int _kbhit (void);

Fichier kbhit.cpp


/**
 *
 * \file    kbhit.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la fonction _kbhit()
 *
 **/
#include 
#include 
#include 
#include 
#include 

using namespace std;
 
void set_mode (bool want_key) {
	static struct termios old, cur;
	if (!want_key) {
		tcsetattr (STDIN_FILENO, TCSANOW, &old);
		return;
	}
 
	tcgetattr (STDIN_FILENO, &old);
	cur = old;
	cur.c_lflag &= ~(ICANON | ECHO);
	tcsetattr (STDIN_FILENO, TCSANOW, &cur);
}
 
int _kbhit (void) {
	int c = 0;
	struct timeval tv;
	set_mode (true);
	fd_set fs;
	tv.tv_usec = tv.tv_sec = 0;
 
	FD_ZERO (&fs);
	FD_SET  (STDIN_FILENO, &fs);
	select  (STDIN_FILENO + 1, &fs, 0, 0, &tv);
 
	if (FD_ISSET (STDIN_FILENO, &fs)) {
		c = cin.get ();
		set_mode (false);
	}
	return c;
	
} // _kbhit()

Fichier ObjetTempo.h


/**
 *
 * \file    ObjetTempo.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe ObjetTempo
 *
 **/
#ifndef _OBJETTEMPO
#define _OBJETTEMPO

#include "Chrono.h"

namespace nsDigicode {

	class ObjetTempo {
		int delai;
	  protected:
		bool    attente;
		Chrono* leChrono;
	  public:
		Chrono* getChrono (void);
		void    finTempo  (void);
		ObjetTempo (Chrono*, int);
		int getDelai (void) const;
		
	}; // ObjetTempo
	
} /* ns_Digicode */

#endif /* _OBJETTEMPO */

Fichier ObjetTempo.cpp


/**
 *
 * \file    ObjetTempo.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe ObjetTempo
 *
 **/
#include "ObjetTempo.h"                  
#include "Chrono.h"
using namespace std;

nsDigicode::ObjetTempo::ObjetTempo (Chrono* lech, int d) :
	leChrono (lech),
	delai (d) {}  

void nsDigicode::ObjetTempo::finTempo (void) { attente = false; }  

int nsDigicode::ObjetTempo::getDelai (void) const { return delai; }   

nsDigicode::Chrono* nsDigicode::ObjetTempo::getChrono (void) { return leChrono; }   

Fichier Porte.h


/**
 *
 * \file    Porte.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Porte
 *
 **/
#ifndef _PORTE
#define _PORTE

#include "ObjetTempo.h"
#include "Capteur.h"
#include "Chrono.h"

namespace nsDigicode {

	class Porte : public ObjetTempo {
		Capteur leCapteur;
		bool fermer();
	  public:
		bool ouvrir();
		Porte(Chrono*, int);
		
	}; // Porte
	
} /* ns_Digicode */

#endif /* _PORTE */

Fichier Porte.cpp


/**
 *
 * \file    Porte.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Porte
 *
 **/
#include 

#include "Porte.h"                  
using namespace std;

nsDigicode::Porte::Porte (Chrono * lech, int d) : ObjetTempo(lech, d) {}  

bool nsDigicode::Porte::ouvrir (void) {
	attente = true;
	leChrono->demarrer(this);
	cout << "Vous pouvez entrer" << endl;
	while (attente && !leCapteur.detecter());
	if (attente)
		return fermer ();
	else {
		cout << "Temps écoulé - La porte se verrouille" << endl;
		return true;
	}
} // ouvrir()

bool nsDigicode::Porte::fermer (void)  
{
	cout << "Porte ouverte : fermez la porte" << endl;
	while (attente && !leCapteur.detecter ());
	if (attente)
	{
		leChrono->arreter ();
		attente = false;
		cout << "Vous avez fermé la porte" << endl;
		return true;
	}
	else
		return false;
}  // fermer()

Fichier Voyant.h


/**
 *
 * \file    Voyant.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Voyant
 *
 **/
#ifndef _VOYANT
#define _VOYANT

#include 

namespace nsDigicode {

	enum Color {rouge,  vert};
	enum Etat  {allume, eteint};
	
	class Voyant {
		Color couleur;
		Etat et;
	  public:
		void allumer  (void);
		void eteindre (void);
		Voyant (Color c, Etat e);
		std::string getStatut (void) const;
		
	}; // Voyant
	
} /* ns_Digicode */

#endif /* _VOYANT */

Fichier Voyant.cpp


/**
 *
 * \file    Voyant.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Voyant
 *
 **/

#include "Voyant.h"                  

nsDigicode::Voyant::Voyant(Color c, Etat e) {  
	couleur = c;
	et = e;
	
}  // Voyant()

void nsDigicode::Voyant::allumer (void) { 
	if (et == eteint)
		et = allume;
	
}  // allumer()

void nsDigicode::Voyant::eteindre (void) {
	if (et == allume)
		et = eteint;
	
} // eteindre()

string nsDigicode::Voyant::getStatut (void) const {
	if (et == allume) return "ON";
	return "OFF";
	
} // getStatut()

Fichier Systeme.h


/**
 *
 * \file    Systeme.h
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  déclaration de la classe Systeme
 *
 **/
#ifndef _SYSTEME
#define _SYSTEME

#include "Interface.h"
#include "Porte.h"
#include "Alarme.h"
#include "BD.h"
#include "Archivage.h"
#include "Chrono.h"

namespace nsDigicode {

	class Systeme {
		Interface lInterface;
		Porte     laPorte;
		Alarme    lAlarme;
		BD        laBD;
		Archivage lArchive;
		Chrono    leChrono;
	  public:
		Systeme (int, int, int);
		
	}; // Systeme
	
} /* ns_Digicode */

#endif /* _SYSTEME */

Fichier Systeme.cpp


/**
 *
 * \file    Systeme.cpp
 *
 * \authors S. Nabitz
 *
 * \date    04/04/2018
 *
 * \version V1.0
 *
 * \brief  définition de la classe Systeme
 *
 **/
#include                   
using namespace std;

#include "Systeme.h"                  

#define DELAI_CLAVIER 5
#define DELAI_PORTE 8
#define NB_CHIFFRES_CLAVIER 4

nsDigicode::Systeme::Systeme (int dc, int dp, int nbca) :
		lInterface (&leChrono, dc, nbca), laPorte (&leChrono, dp) { 	
	int c;
	while (true) {
	   lArchive.archiver (c = lInterface.saisieCode());
	   if (laBD.verifier (c)) {
		   if (!laPorte.ouvrir ()) 
			   lAlarme.declencher ();
	   }
	}
} // Systeme()

int main (void) {
	nsDigicode::Systeme s (DELAI_CLAVIER, DELAI_PORTE, NB_CHIFFRES_CLAVIER);
	
	return 0;
	
} // main()

M2103-TP4-Exo-1

Dès qu’elle atteint une certaine taille, il est important qu’une application soit décomposée en plusieurs niveaux hiérarchiques clairs, pour sa lisibilité, sa maintenabilité et son évolutivité.

Le plus haut niveau est la fonction main().

C’est par l’exécution de cette fonction que commence (presque …) l’application.

Il est possible de lui faire passer des informations (des arguments) au lancement de la commande correspondante : options, nombres, noms de fichiers, etc. :

CopyFile FicDest FicSource -b

Cette possibilité a déjà été étudiée.

Le rôle de la fonction main() est donc de commencer par analyser et valider ces arguments.

Puis vient l’exécution d’une ou de plusieurs fonctions relativement générales (pour nous les fonctions testXxx()).

Enfin, avant de “rendre la main” au système, la fonction main() doit clore proprement l’application :

  • ne pas laisser remonter une exception non capturée, qui provoquerait le désagréable et sibyllin message :
    abort
    

    sans autre explication,

  • préparer le “compte-rendu” de l’exécution sous la forme d’un entier renvoyé à la procédure appelante (en général le shell) : la fameuse variable $?.

En d’autres termes, une application pourrait avoir la structure suivante :

...
int main (...)
{
    validerArguments ();
    initialisation ();

    phase_1 ();
    ...
    phase_N ();

    terminaison ();

    return compteRendu;

} // main()

En principe, dans une application idéale, toutes les exceptions levées devraient être traitées localement, ou à un niveau hiérarchique supérieur, mais aucune ne devraient remonter jusqu’à la fonction main().

On peut cependant supposer :

  • que certaines exceptions sont levées “à l’insu” du développeur, et non capturées par la suite (nul n’est parfait !)
  • que certaines exceptions sont délibérément ignorées et non capturées car l’erreur est tellement grave qu’aucun traitement ne peut la corriger et que la seule chose à faire est de laisser se terminer l’application.

Chaque étape de l’application étant susceptible de lever une exception, la structure de la fonction main() devrait être la suivante :

...
int main (...)
{
    try
    {
        validerArguments();
        initialisation();

        phase_1();
        ...
        phase_N();

        terminaison();

        return  compteRenduDeSucces ;  // = 0
    }
    catch (...a_preciser...)
    { 
        affichageApproprie ();

        return  compteRenduDEchec ;   //  O < n < 255
    }

} // main()

Tout objet ou toute valeur peut servir d’exception.

Cependant, pour suivre les recommandations de la norme de C++ et de tous les spécialistes, les seules exceptions à utiliser devraient être les exceptions standard ou de classes dérivées par l’utilisateur de la classe standard exception, ce qui est le cas de notre classe CException.


Nous nous limiterons donc à capturer trois sortes d’exceptions :

  • les exceptions standard,
  • nos exceptions CException,
  • toutes les autres.

En cas de capture d’une exception inconnue, nous utiliserons la constante KExcInconnue (= 255) comme compteRenduDEchec.

En cas de capture d’une exception standard exception, nous utiliserons la constante KExcStd (= 254) comme compteRenduDEchec,

En cas de capture d’une exception CException, nous utiliserons la donnée-membre myCodErr comme compteRenduDEchec.

En cas de déroulement normal, nous utiliserons les constantes KNoError ou KNoExc (= 0) comme compteRenduDeSucces.

Nous considèrerons que toutes les fonctions de niveau immédiatement inférieur à main() (validerArguments(), initialisation(), phase_1(), phase_N(), terminaison() dans l’exemple ci-dessus, testXxx() dans les TPs habituels – comme nous l’avons toujours fait), sont susceptibles de lever n’importe quelle exception.

En conséquence, nous ne donnerons aucune indication d’exception dans leur profil :

void testXxx (void)
{
    ...

et non

void testXxx (void) throw (n_importe_quoi)
{
    ...

Travail à réaliser

Créez le projet ExceptionsInMain.

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 <exception>
#include <string>

#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 <iostream>
#include <string>

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

(toutes les constantes représentant des codes d’erreurs, quels qu’ils soient, dans toutes les applications futures qui vous seront proposées, seront ajoutées dans ce fichier qui sera donc inclus très fréquemment).

Dans l’espace de noms anonyme du fichier testExceptionsInMain.cpp, écrire la fonction testExceptionsInMain() qui lève (schéma de choix) :

  • soit une exception de base, en appelant le constructeur exception() de la classe exception,
  • soit une exception standard plus spécifique,
    • soit directement en appelant par exemple le constructeur runtime_error() de la classe runtime_error et en lui passant un paramètre effectif (un libellé),
    • soit indirectement en appelant par une fonction dont vous savez qu’elle lève un exception standard, par exemple la fonction at() de la classe string avec un indice invalide, qui lève une exception out_of_range,
  • soit une exception CException,
  • soit une exception quelconque (un entier par exemple).

Dans la fonction main(), effectuer les modifications correspondantes aux indications ci-dessus en capturant dans l’ordre :

  1. les exceptions CException,
  2. les exceptions spécifiques standard (runtime_error ou autre),
  3. les exceptions standard (exception),
  4. toutes les autres exceptions.

Compilez et testez.

Variante 1

Dans la fonction main(), permutez la capture des exceptions CException et des exceptions runtime_error et testez à nouveau.

Vous devez ne constater aucun changement.

Variante 2

Avant la capture de toute exception (catch (...)), ajoutez la capture d’un unsigned et levez une exception entière.

Vous constatez qu’elle n’est pas capturée : il n’y a aucun transtypage/conversion entre les exceptions levées et les exceptions capturées.

Remplacez la capture d’un unsigned par celle d’un int et répétez le test.

L’exception est correctement capturée.

Variante 3

Dans la fonction main(), permutez la capture des exceptions exception et des exceptions runtime_error et testez à nouveau.

Vous devez constater que les exceptions runtime_error ne sont plus jamais capturées (ce qui vous est d’ailleurs indiqué par un message warning lors de la compilation).

Ceci est dû au fait qu’une exception runtime_error est d’une classe dérivée de exception, donc plus spécifique, et qu’une instruction catch qui capture les exceptions exception capturent aussi les exceptions dérivées.

Vous remarquez cependant que, bien que capturant apparemment une exception, le programme affiche cependant le
string que vous avez passé au constructeur de runtime_error.

C’est du polymorphisme.

En conséquence, vous ne garderez à l’avenir que les captures des deux exceptions CException et exception, et toutes les autres.

Variante 4

Au lieu de lever directement une exception runtime_error, provoquez une exception par une fonction standard : utilisez la fonction at() membre de la classe string avec une valeur d’indice invalide.

Compilez et testez

Ne pas oublier de sauvegarder les fichiers sources du projet sur github.

M2103-TP4-Exo-1-Corrigé

namespace
{
    void clearScreen (void)
    {
         cout << "\033[2J\033[1;1H" << flush;

    } // clearScreen()

    char choixDansMenu (void)
    {
        clearScreen();

        cout << "A : exception 'exception'\n"
                "B : exception standard specifique\n"
                "C : exception 'CException'\n"
                "D : exception inconnue\n\n"
                "Votre choix (suivi de ) : ";

        char choix;
        cin >> choix;

        clearScreen();

        return choix;

    } // ChoixDansMenu()

    void traiterCommande (char cmd)
    {
        switch (cmd)
        {
          case 'A' :
          case 'a' :
            throw exception ();

          case 'B' :
          case 'b' :
            {
              // throw runtime_error ("erreur d'execution ...");
              string s;
              cout << s.at (0);
              break;     // inutile puisqu'une exception est levee avant
            }

          case 'C' :
          case 'c' :
            throw CException ("Surprise, surprise !", 123);

          case 'D' :
          case 'd' :
            throw 123;
        }

    } // traiterCommande()

    void testExceptionsInMain ()
    {
         for ( ; ; ) traiterCommande (choixDansMenu());

    } // testExceptionsInMain()

} // namespace

int main ()
{
    try
    {
        testExceptionsInMain ();

        return KNoExc;
    }
    catch (const CException & e)
    {
        cerr << "Erreur        : " << e.getLibelle () << '\n' 
             << "Code d'erreur = " << e.getCodErr ()  << '\n';
        return e.getCodErr();
    }    
    catch (const out_of_range & e)    // levee par string::at()
    {
        cerr << "Exception out_of_range : " << e.what () << '\n'; 
        return KExcStd;
    } 
    */   
    /*
    catch (const runtime_error & e)
    {
        cerr << "Exception runtime_error : " << e.what () << '\n'; 
        return KExcStd;
    } 
    */   
    catch (const exception & e)
    {
        cerr << "Exception standard : " << e.what () << '\n'; 
        return KExcStd;
    }    
    /*
    catch (const unsigned & e)
    {
        cerr << "Exception unsigned : " << e << '\n'; 
        return KExcStd;
    }    
    catch (const int & e)
    {
        cerr << "Exception int : " << e << '\n'; 
        return KExcStd;
    }
    */   
    catch (...)
    {
        cerr << "Exception inconnue\n";
        return KExcInconnue;
    }    

} // main()

M2103-TP4-Exo-2

Jusqu’à présent, nous avons vu qu’il est toujours possible de consulter l’état d’un flux pour tester la fin de fichier ou vérifier a posteriori la réussite ou l’échec d’une opération, d’extraction par exemple.

Dans tous les cas, cela demande au développeur une démarche active : c’est à lui de vérifier à chaque opération l’état du flux.

Le C++ offre une possibilité très intéressante : demander au flux de “prévenir” en cas de problème.

Cela est effectué au moyen d’exceptions : le flux lève une exception standard de la classe ios_base::failure (dérivée de la classe standard exception) dès qu’une opération échoue.

Le développeur n’a plus qu’à inclure son code dans un bloc try et à prévoir un bloc catch pour capturer l’exception et effectuer le traitement approprié sur le flux.

Mais ce n’est pas le comportement par défaut du flux : il faut l’informer en appelant sa fonction membre exceptions(), à laquelle on passe en paramètre la “liste” (une combinaison de flags) des événements susceptibles de déclencher une exception.

Ces événements possibles sont au nombre de 3 :

  • “fin-de-fichier”, indiqué par le flag ios_base::eofbit,
  • un échec d’opération de lecture (extraction) ou d’écriture (injection), indiqué par le flag ios_base::failbit,
  • un échec d’opération dû à un fichier corrompu (en principe problème matériel), indiqué par le flag ios_base::badbit.

L’extrait de code suivant montre comment le flux standard d’entrée cin est informé qu’il doit lever une exception dans l’un des deux premiers cas :

cin.exceptions (ios_base::failbit | ios_base::eofbit);

Créez le projet Failure.

Dans le projet Failure, copiez le contenu des fichiers
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__  */

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 <exception>
#include <string>

#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
SqueletteMain.cpp :

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

#include <iostream>
#include <exception>

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

using namespace std;
using namespace nsUtil;

namespace
{
    void testFailure (void)
    {
		//TODO
		
    } // TestFailure  ()

} // namespace

int main (void)
{
    try
    {
        testFailure ();

        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()

Renommer SqueletteMain.cpp en TestFailure.cpp.

Dans la fonction TestFailure() du fichier TestFailure.cpp, positionner le flux standard d’entrée pour qu’il lève une exception en cas de fin de fichier et d’échec d’extraction.

Ajouter un bloc try contenant une boucle infinie de lecture d’un entier au clavier.

Ajouter un bloc catch capturant l’exception levée par le flux, l’afficher (fonction membre what()), puis consulter l’état du flux (fail() ou eof()) pour afficher le diagnostic d’arrêt de la boucle.

Après cet affichage, propager l’exception (throw tout court).

Vous devez constater que vous capturez à nouveau cette exception dans la fonction main().

Ne pas oublier de sauvegarder les fichiers sources du projet sur github.