Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Gestionnaire d'exceptions générique

17 réponses
Avatar
Luc Hermitte
Bonjour,

Suite =E0 une astuce que j'avais crois=E9e sur la FAQ C++-lite [1],
j'avais commenc=E9 =E0 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=E0, 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
=EAtre intercept=E9, un coup d'autres types doivent =EAtre convertis vers un
type tier, etc., etc.

Du coup, je me posais la question d'avoir un truc encore plus
g=E9n=E9rique, 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=E9es
TYPE_LIST_3(Metier::ErrorCppFormat, std::bad_catch,
std::exception)// exceptions =E0 convertir
>
(stringContexte, divers bool=E9ns pour les traces);
}

Ce qui en gros serait =E9quivalent (c=F4t=E9 HandleException) =E0 :

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=E8me est "comment y arriver?". Sans partir sur la
g=E9n=E9ration d'un mini HandleException pour chaque type des TYPE_LIST
(i.e. le catch(...) de chaque HandleException appelant un autre
HandleException o=F9 l'on aurait d=E9pil=E9 un type d'exception de la
liste), ou sans partir sur des macros =E0 profusion, je s=EAche un peu.
(Je ne suis pas s=FBr que la premi=E8re solution usine =E0 gaz soit tr=E8s
efficace, ni tr=E8s maintenable. Quant =E0 la la deuxi=E8me, elle peut
rapidement devenir un cauchemar =E0 debugguer le jour o=F9 cela devient
n=E9cessaire (core)).


Bref, Vous =EAtes-vous d=E9j=E0 pos=E9 ce probl=E8me ? Et si oui, =E0 quelle
conclusion avez-vous abouti?


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

--
Luc Hermitte

10 réponses

1 2
Avatar
Mathias Gaunard

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.


Avatar
Luc Hermitte
On 10 sep, 13:36, Mathias Gaunard wrote:
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

Avatar
Jean-Marc Bourguet
Luc Hermitte writes:

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

Avatar
Luc Hermitte
On 10 sep, 14:46, Jean-Marc Bourguet wrote:
Luc Hermitte writes:

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


Avatar
Fabien LE LEZ
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());
}

Avatar
Luc Hermitte
Salut,

On 10 sep, 17:51, Fabien LE LEZ wrote:
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

Avatar
Fabien LE LEZ
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.

Avatar
Jean-Marc Bourguet
Fabien LE LEZ writes:

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

Avatar
Luc Hermitte
On 11 sep, 20:59, Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

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


Avatar
Fabien LE LEZ
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.

1 2