Il peut être un peu pénible de tracer rapidement d'où vient une exception, notamment du fait que les informations sur l'emplacement de sa déclaration ne sont pas immédiatement accessibles.
Bien sûr, on peut facilement récupérer ce contexte en utilisant le débogueur, mais cela reste difficile pour de simples utilisateurs. Un bon compromis est d'ajouter une forme de lien vers le code source directement dans le message, facile à copier/coller.
C'est ce que propose cette très (très) léger outils : exceptions.h. Il permet d'inclure très facilement le nom de la fonction, le fichier et le numéro de ligne d'où provient l'exception, tout en facilitant leur usage (déclaration et instanciation).
Par exemple, ce code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
#include <iostream>
#include <iomanip>
#include "exceptions.h"
// Une macro permet de faciliter la création d'une hiérarchie d'exceptions
EXCEPTION( Exception, Observation_Existentielle );
EXCEPTION( Observation_Existentielle, Observation_Bouddhiste );
EXCEPTION( Observation_Bouddhiste, Observation_Zen );
EXCEPTION( Observation_Existentielle, Observation_Pastafariste );
int main()
{
try {
double pi = 3.1415926535;
// Une autre macro permet de facilement lever une exception avec un message construit dynamiquement
RAISE( Observation_Pastafariste, std::setprecision(3) << "Les emmerdes, ça arrive avec " << pi << " appendices nouillesques" );
} catch( Observation_Existentielle & e ) {
std::cerr << e.what() << std::endl;
}
} |
Affichera :
Les emmerdes, ça arrive avec 3.14 appendices nouillesques (<Observation_Pastafariste> in main at example.cpp:18)
Explications
Déclaration
La classe de base utilisée pour mettre en place la hiérarchie hérite de std::exception, et utilise l'interface what, plus ou moins de la même manière que std::runtime_error et std::logical_error.
La macro EXCEPTION(Observation_Existentielle, Observation_Pastafariste );, permet de déclarer en une seule ligne (il en faudrait beaucoup plus sans) l'exception Observation_Pastafariste comme dérivant de Observation_Existentielle. Ainsi, si vous interceptez des instances de la classe Observation_Existentielle, vous intercepterez une instance de la classe Observation_Pastafariste.
Localisation
Pour ajouter les informations sur l'endroit d'où provient l'exception, on utilise les macros __FUNCTION__, __FILE__ et __LINE__, qui sont gérées par le compilateur lui-même. La classe Exception permet de les passer au constructeur (soit directement, soit à l'aide d'une autre macro plus courte : E_INFOS), vous pourriez par exemple faire :
throw Observation_Existentielle( "Les emmerdes, ça arrive", E_INFOS );
Utilisation
Pour faciliter encore un peu plus l'emploi de cette construction, une dernière macro permet d'inclure automatiquement toutes ces informations, sans avoir à les taper :
RAISE( Observation_Zen, "Quel est le son des emmerdes qui arrivent ?" );
Et y ajoute la possibilité d'utiliser un message à base de flux, sans avoir à le déclarer :
RAISE( Observation_Bouddhiste, "Il y a" << 0 << " emmerdes qui arrivent, en réalité." );
À comparer avec la version que vous devriez taper, sans exceptions.h :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <stdexcept>
class Observation_Existentielle : public std::runtime_error
{ public:
Observation_Existentielle( const std::string & what_arg ) : std::runtime_error(what_arg) {}
};
class Observation_Bouddhiste : public Observation_Existentielle
{ public:
Observation_Bouddhiste( const std::string & what_arg ) :Observation_Existentielle(what_arg) {}
};
int main()
{
try {
std::ostringstream msg;
double vacuite = 0;
// Notez que le nom de l'exception est codé en dur et doit être maintenu 2 fois...
msg << "Il y a " << vacuite << " emmerdes qui arrivent (<Observation_Bouddhiste> in " << __FUNCTION__ << " at " << __FILE__ << ":" << __LINE__ << ")";
// Sans oublier la conversion en string.
throw( Observation_Bouddhiste( msg.str() ) );
} catch( Observation_Existentielle & e ) {
std::cerr << e.what() << std::endl;
}
} |
Partager