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.