Gestionnaire d'exceptions générique

Le
Luc Hermitte
Bonjour,

Suite à une astuce que j'avais croisée sur la FAQ C++-lite [1],
j'avais commencé à factoriser ma gestion des exceptions. Ce qui me
permet de ne pas parasiter mes fonctions d'interface (CORBA), et
autres fonctions de thread, de blocs try-catch plus longs que le corp
utile de la fonction.

Jusque là, tout va bien. Sauf que. Toutes ces fonctions
handleException() se ressemblent beaucoup tout en connaissant des
subtiles variations. Un coup un type d'exception doit circuler sans
être intercepté, un coup d'autres types doivent être convertis vers un
type tier, etc., etc.

Du coup, je me posais la question d'avoir un truc encore plus
générique, quelque chose qui pourrait p.ex. s'utiliser de la sorte:

try {
actions
} catch () {
handleException<
ExceptionToThrow, // vers laquelle on convertit,
possiblement void
TYPE_LIST_2(CORBA::SystemException,
Metier::ErrorCorbaFormat), // exceptions non filtrées
TYPE_LIST_3(Metier::ErrorCppFormat, std::bad_catch,
std::exception)// exceptions à convertir
>
(stringContexte, divers booléns pour les traces);
}

Ce qui en gros serait équivalent (côté HandleException) à :

try {
throw;
} catch (CORBA::SystemException const&e ) {
if (trace) TRACE(contexte,
exception_traits<CORBA::SystemException>::toString(e));
throw;
} catch (Metier::ErrorCorbaFormat const&e ) {
if (trace) TRACE(contexte,
exception_traits<Metier::ErrorCorbaFormat>::toString(e));
throw;
} catch (Metier::ErrorCppFormatconst&e ) {
if (trace) TRACE(contexte,
exception_traits<Metier::ErrorCppFormat>::toString(e));
throw exception_traits<ExceptionToThrow>::build(
exception_traits<Metier::ErrorFormatCpp>::toString(e));
}


Mon gros problème est "comment y arriver?". Sans partir sur la
génération d'un mini HandleException pour chaque type des TYPE_LIST
(i.e. le catch() de chaque HandleException appelant un autre
HandleException où l'on aurait dépilé un type d'exception de la
liste), ou sans partir sur des macros à profusion, je sêche un peu.
(Je ne suis pas sûr que la première solution usine à gaz soit très
efficace, ni très maintenable. Quant à la la deuxième, elle peut
rapidement devenir un cauchemar à debugguer le jour où cela devient
nécessaire (core)).


Bref, Vous êtes-vous déjà posé ce problème ? Et si oui, à quelle
conclusion avez-vous abouti?


[1] http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.9

--
Luc Hermitte
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Mathias Gaunard
Le #311478

try {
actions...
} catch (...) {
handleException<
ExceptionToThrow, // vers laquelle on convertit,
possiblement void
TYPE_LIST_2(CORBA::SystemException,
Metier::ErrorCorbaFormat), // exceptions non filtrées
TYPE_LIST_3(Metier::ErrorCppFormat, std::bad_catch,
std::exception)// exceptions à convertir

(stringContexte, divers booléns pour les traces);

}


Plutôt que d'utiliser ces macros vieillots pour faire des typelists,
utilise Boost.MPL, qui contient tout un tas de conteneurs et
d'algorithmes associés pour que tu puisses manipuler aisément tes types
et pouvoir générer le code que tu souhaites sans soucis.


Luc Hermitte
Le #311475
On 10 sep, 13:36, Mathias Gaunard
Plutôt que d'utiliser ces macros vieillots pour faire des typelists,
utilise Boost.MPL, qui contient tout un tas de conteneurs et
d'algorithmes associés pour que tu puisses manipuler aisément tes typ es
et pouvoir générer le code que tu souhaites sans soucis.


Avec mon mon vieux compilo de chez Sun, boost n'est pas envisageable
-- sans compter d'autres histoires de dépendances à des bibliothèques
tierces. Je me retrouve sans cesse à devoir réinventer la roue en
pierre.
:-(

Ceci dit, si je ne me trompe pas, cela ne résoudrait pas mon problème
de passer d'un ensemble de types à une série de catchs

Jean-Marc Bourguet
Le #311474
Luc Hermitte
Mon gros problème est "comment y arriver?". Sans partir sur la
génération d'un mini HandleException pour chaque type des TYPE_LIST
(i.e. le catch(...) de chaque HandleException appelant un autre
HandleException où l'on aurait dépilé un type d'exception de la
liste), ou sans partir sur des macros à profusion, je sêche un peu.
(Je ne suis pas sûr que la première solution usine à gaz soit très
efficace, ni très maintenable. Quant à la la deuxième, elle peut
rapidement devenir un cauchemar à debugguer le jour où cela devient
nécessaire (core)).


Je ne vois meme pas comment faire avec des macros.

Est-ce que la premiere solution est vraiment plus usine a gaz et moins
maintenable que les autres solutions? _A priori_, je ne le pense pas.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Luc Hermitte
Le #311472
On 10 sep, 14:46, Jean-Marc Bourguet
Luc Hermitte
Mon gros problème est "comment y arriver?". Sans partir sur la
génération d'un mini HandleException pour chaque type des TYPE_LIST
(i.e. le catch(...) de chaque HandleException appelant un autre
HandleException où l'on aurait dépilé un type d'exception de la
liste), ou sans partir sur des macros à profusion, je sêche un peu.
(Je ne suis pas sûr que la première solution usine à gaz soit tr ès
efficace, ni très maintenable. Quant à la la deuxième, elle peut
rapidement devenir un cauchemar à debugguer le jour où cela devient
nécessaire (core)).


Je ne vois meme pas comment faire avec des macros.


En se basant sur les type lists, on y arrive assez "simplement".

En gros, cela donne (je vous dispense de tous les traits et cie)
----------------- >% -----------------
#define HANDLE_EXCP(TT_, trace_, TE_)
catch (TE_ const& e_) {
Exception::Tracer<trace_>::trace(e_);
Exception::Raiser<TT_>::raise(e_);
}

#define EXCP_LIST1(TT_, trace_, T1_)
HANDLE_EXCP(TT_, trace_, T1_)

#define EXCP_LIST2(TT_, trace_, T1_, T2_)
EXCP_LIST1(TT_, trace_, T1_)
HANDLE_EXCP(TT_, trace_, T2_)

#define EXCP_LIST3(TT_, trace_, T1_, T2_, T3_)
EXCP_LIST2(TT_, trace_, T1_, T2_)
HANDLE_EXCP(TT_, trace_, T3_)

#define EXCP_LIST4(TT_, trace_, T1_, T2_, T3_, T4_)
EXCP_LIST3(TT_, trace_, T1_, T2_, T3_)
HANDLE_EXCP(TT_, trace_, T4_)

namespace Exception {
void HandleLog() {
try {
throw;
}
EXCP_LIST4(void, true, int, std::bad_alloc, std::range_error,
std::exception);
}
void HandleConvert() {
try {
throw;
}
// convertit les int en range_error
EXCP_LIST1(std::range_error, true, int)
// trace (et "sinke") les autres exceptions
EXCP_LIST2(void, true, std::bad_alloc, std::exception);
}
} // Exception namespace
----------------- >% -----------------

Cela permet de factoriser les écritures, mais ... à la moindre erreur
dans un des traits ou ailleurs, la compréhension de ce qui a cloché
est rapidement éprouvante.


Est-ce que la premiere solution est vraiment plus usine a gaz et moins
maintenable que les autres solutions? _A priori_, je ne le pense pas.


Entre ça et les macros, mon coeur balance, je vois des inconvénients
des deux côtés.

--
Luc Hermitte


Fabien LE LEZ
Le #311431
On Mon, 10 Sep 2007 10:09:38 -0000, Luc Hermitte :

try {
throw;
} catch (CORBA::SystemException const&e ) {
if (trace) TRACE(contexte,
exception_traits<CORBA::SystemException>::toString(e));
throw;
} catch (Metier::ErrorCorbaFormat const&e ) {
if (trace) TRACE(contexte,
exception_traits<Metier::ErrorCorbaFormat>::toString(e));
throw;
} catch (Metier::ErrorCppFormatconst&e ) {
if (trace) TRACE(contexte,
exception_traits<Metier::ErrorCppFormat>::toString(e));




Je verrais bien une fonction unique qui se chargerait de transformer
tous les types d'exception en std::exception.



class ExceptionCorba: public std::exception
{
public:
ExceptionCorba (CORBA::SystemException const&);
};

class ExceptionCorbaFormat: public std::exception
{
public:
ExceptionCorbaFormat (Metier::ErrorCorbaFormat const&);
};

class ExceptionAutre: public std::exception
{
public:
char const* what() { return "Unknown exception"; }
};

etc.



template <class Foncteur> void AppelleFoncteur (Foncteur f)
{
try
{
f();
}
catch (CORBA::SystemException const& e)
{
throw ExceptionCorba (e);
}
catch (Metier::ErrorCorbaFormat const& e)
{
throw ExceptionCorbaFormat (e);
}
//etc.
catch (...)
{
throw ExceptionAutre();
}
}




Ainsi, au lieu d'écrire

try
{
machin();
}
catch (xxx) { ... }
catch (yyy) { ... }
catch (zzz) { ... }
catch (...) { ... }

à chaque fois, tu peux te contenter de :

try
{
AppelleFoncteur (machin);
}
catch (std::exception const& e)
{
EnregistreLog (e.what());
}

Luc Hermitte
Le #311428
Salut,

On 10 sep, 17:51, Fabien LE LEZ
Je verrais bien une fonction unique qui se chargerait de transformer
tous les types d'exception en std::exception.
[snip une solution]


Disons que cela ne change guère de ce que j'ai fait déjà avec mes
fonctions HandleExceptions.
Seulement, dans une même unité de traduction, suivant le contexte,
j'ai 3 ou 4 HandleExceptions subtilement différents à écrire. (un qui
absorbe tout pour le destructeur, un qui converti les erreurs CORBA en
erreurs C++, et un qui converti les erreurs C++ en erreurs CORBA, un
qui converti tout en MessageBlock à destination d'autres tâches
(threads), ...)

Ce que je cherche à faire, consiste en fait à factoriser tout le code
commun que je retrouve dans les HandleExceptions. En gros avoir un
HandleException générique où les points de variablilité (que je p êche
à gérer proprement) sont des types exception en nombre inconnu à
l'avance.

--
Luc Hermitte

Fabien LE LEZ
Le #311426
On Tue, 11 Sep 2007 07:41:07 -0700, Luc Hermitte

Disons que cela ne change guère de ce que j'ai fait déjà avec mes
fonctions HandleExceptions.


C'est principalement le point de vue qui change.

De mon point de vue (mais c'est sujet à discussion), si une exception
ne dérive pas de std::exception, c'est un bug. Il faut donc le
réparer, soit en modifiant la classe "exception CORBA", soit en
l'encapsulant ou en la transformant d'une manière ou d'une autre.
Comme la première solution n'est probablement pas possible ici, je
contourne le bug en transformant une exception_CORBA en
std::exception.

Tu n'as plus alors à te préoccuper que d'un seul type, ce qui te
permet de n'avoir qu'un seul bloc catch.

Jean-Marc Bourguet
Le #311425
Fabien LE LEZ
Tu n'as plus alors à te préoccuper que d'un seul type, ce qui te permet
de n'avoir qu'un seul bloc catch.


Qu'est-ce que ça change? Il a des traitements différents à faire suivant
les exceptions.

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Luc Hermitte
Le #311424
On 11 sep, 20:59, Jean-Marc Bourguet
Fabien LE LEZ
Tu n'as plus alors à te préoccuper que d'un seul type, ce qui te pe rmet
de n'avoir qu'un seul bloc catch.


Qu'est-ce que ça change? Il a des traitements différents à faire s uivant
les exceptions.


Tout à fait.
Ceci dit, je n'avais pas du tout suivit où Fabien voulait en venir.
Il faut que je creuse pour voir si je peux en faire quelque chose ou
pas -- la tête dans le guidon, on passe parfois à côté d'évidence s.
Entre les 4 frontières où il faut d'un d'un type ou d'un autre, et les
endroits plus précis dans le code où les traitements sont à
spécialiser, je me retrouve à devoir marcher sur des oeufs.

--
Luc Hermitte


Fabien LE LEZ
Le #311423
On 11 Sep 2007 20:59:07 +0200, Jean-Marc Bourguet
Qu'est-ce que ça change?


Je suis sans doute passé à côté du problème ; toutefois, pour répondre
à ta question, il y a bien un changement : on passe d'un polymorphisme
statique (types indépendants, templates) à un polymorphisme dynamique
(héritage, fonctions virtuelles).
Je ne sais pas si ça aidera l'OP, mais ça peut être une piste.

Publicité
Poster une réponse
Anonyme