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

pb de portée des exceptions

10 réponses
Avatar
Marc G
bonsoir,
j'ai un programme avec des modules.

Si je simplifie :
- dans un fichier module1.hpp j'ai une méthode qui lance une exception
void f()
{
throw CException("blabla");
}

- dans un fichier module2.cpp, j'ai une méthode qui appelle f
void g()
{
f();
}

- dans un fichier module3.cpp, j'ai une méthode qui appelle g
void h()
{
try {
g();
}
catch(CException &e)
{
output << e.what();
}
}

par le jeu des inclusions, la méthode h connait g qui connait f => pas de pb
à la compilation
mais à l'exécution, ça plante. En fait e.what() affiche n'importe quoi et le
programme devient instable.
Pour que ça marche, il faut que j'écrive g comme suit :
void g()
{
try {
f();
}
catch(CException &) { throw; }
}
c'est-à-dire qu'il faut que j'intercepte l'exception pour la relancer.
Je comprends pas bien pourquoi.
C'est la norme ou mon compilo qui merdouille ?
Merci à vous.

10 réponses

Avatar
IR
Marc G wrote:
void h()
{
try {
g();
}
catch(CException &e)
{
output << e.what();
}
}

par le jeu des inclusions, la méthode h connait g qui connait f =>
pas de pb à la compilation
mais à l'exécution, ça plante. En fait e.what() affiche n'importe
quoi et le programme devient instable.
Pour que ça marche, il faut que j'écrive g comme suit :
void g()
{
try {
f();
}
catch(CException &) { throw; }
}
c'est-à-dire qu'il faut que j'intercepte l'exception pour la
relancer. Je comprends pas bien pourquoi.
C'est la norme ou mon compilo qui merdouille ?


A vue de nez il s'agit de ton compilo...

Wild guess... tu utilises VC6? (le nommage CException me fait penser
à MS, et il n'y a que le 6 qui puisse être aussi buggé :-p)


Cheers,
--
IR

Avatar
Marc G
Wild guess... tu utilises VC6? (le nommage CException me fait penser
à MS, et il n'y a que le 6 qui puisse être aussi buggé :-p)


non, j'utilise C++ Builder 6 et CException, c'est une classe personnelle
(qui gère des compteurs du nbre d'exceptions levées dans certains cas).
C'est la première fois que je remarque ça et ça m'embête. Donc j'aimerais
bien que ce soit la norme :-)) !
Mon projet commence à devenir "gros" et "compliqué" et s'il devient
bizarroïde, je suis mal parti.
Marc

Avatar
Sylvain
Marc G wrote on 08/03/2007 21:18:
- dans un fichier module1.hpp j'ai une méthode qui lance une exception
void f() {
throw CException("blabla");
}


tu lances ici une instance temporaire.

void h(){
try {
g();
}
catch(CException &e){
output << e.what();
} }


tu veux ici récupérer une référence ... qui a des chances de pointer sur
quoi selon toi ? la pile qui contenait g et f a été dépilé lorsque tu
entres dans ton handler - remplace simplement par catch(CException e)
(s'il s'agit du type des MFC, elles sont correctement copiables).

C'est la norme ou mon compilo qui merdouille ?


on s'oublie souvent dans la liste des sources possibles ...

Sylvain.

Avatar
Sylvain
IR wrote on 08/03/2007 22:40:

Wild guess... tu utilises VC6? (le nommage CException me fait penser
à MS, et il n'y a que le 6 qui puisse être aussi buggé :-p)


médisance pure ! et d'abord VC5 est largement plus buggé.

Sylvain.

Avatar
Fabien LE LEZ
On Thu, 8 Mar 2007 21:18:57 +0100, "Marc G" :

catch(CException &e)


Pourquoi est-ce une référence au lieu d'être une référence const ?
As-tu besoin de modifier e ?

par le jeu des inclusions,


Tu as donc bien la même définition pour CException partout ?
(Avec les #define en pagaille des headers Windows, on est en droit de
se méfier.)

la méthode h connait g qui connait f => pas de pb
à la compilation
mais à l'exécution, ça plante. En fait e.what() affiche n'importe quoi et le
programme devient instable.


Si tu remplaces CException par un truc très simple, ça donne quoi ?

Avatar
James Kanze
On Mar 8, 11:56 pm, Sylvain wrote:
Marc G wrote on 08/03/2007 21:18:

- dans un fichier module1.hpp j'ai une méthode qui lance une exception
void f() {
throw CException("blabla");
}


tu lances ici une instance temporaire.


Qui est copié quelque part ? (Où, c'est un sécret de
l'implémentation.)

void h(){
try {
g();
}
catch(CException &e){
output << e.what();
} }


tu veux ici récupérer une référence ... qui a des chances de poin ter sur
quoi selon toi ?


S'il ne pointe pas sur la copie, l'implémentation est vraiment
bugguée. (En fait, je n'ai jamais entendu parler d'une
implémentation qui avait ce problème.)

la pile qui contenait g et f a été dépilé lorsque tu
entres dans ton handler - remplace simplement par catch(CException e)
(s'il s'agit du type des MFC, elles sont correctement copiables).


C'est pourquoi la norme exige que l'implémentation fait une
copie. Elle définit aussi une durée de vie spéciale aux
exceptions ; l'objet est construit dans l'expression de throw,
et détruit lors qu'on sort du catch (à condition de ne pas avoir
fait throw de nouveau dans le catch).

(Aussi, c'est une de ces copies que le compilateur a droit
d'optimiser. Il se peut donc que dans l'expression :
throw CException("blabla");
on construit directement l'objet qui va servir dans l'exception,
sans passer par un temporaire.)

C'est la norme ou mon compilo qui merdouille ?


on s'oublie souvent dans la liste des sources possibles ...


Une cause possible du problème, évidemment, c'est que son
constructeur de copie n'est pas tout à fait catholique. Mais je
me doute plutôt d'une autre chose : qu'il a compilé g() sans
support pour les exceptions. VC++, au moins, ne l'active pas par
défaut (au moins, pas dans les anciennes versions---je n'ai pas
essayé de compiler avec la nouvelle sans /EHs). Ce qui
expliquerait pourquoi en ajoutant le catch/throw dans g(), ça
marche -- ça lui oblige d'activer le support des exceptions.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Marc G
je finis par être perdu dans les conseils contradictoires.
D'une manière générale, j'essaye de bien faire et quand je lis une
recommandation écrite par une personne "renommée", je m'y plie et je modifie
ma façon de faire.
Donc, dans l'ouvrage
Standards de programmation en C++
Herb Sutter et Andrei Alexandrescu
Collection C++ dirigée par Bjarne Stroustrup
je lis p 174 (j'ai la version française :-))

Lancez par valeur , capturez par référence.
...
Sauf si vous transmettez un pointeur intelligent, ... , vous devez
absolument capturer par référence.

tel quel.
Mon problème reste donc entier.
Mais merci pour tes réflexions.
Marc
Avatar
Marc G
Pourquoi est-ce une référence au lieu d'être une référence const ?
As-tu besoin de modifier e ?


oui, tu as raison, une référence const serait mieux...

par le jeu des inclusions,
Tu as donc bien la même définition pour CException partout ?

(Avec les #define en pagaille des headers Windows, on est en droit de
se méfier.)


hélas oui, j'ai un fichier CException.h que j'inclus partout
en plus, j'ai défini la classe dans un espace de nommage personnel
=> pas de risque de confusion

la méthode h connait g qui connait f => pas de pb
à la compilation
mais à l'exécution, ça plante. En fait e.what() affiche n'importe quoi et
le
programme devient instable.


Si tu remplaces CException par un truc très simple, ça donne quoi ?


tu veux dire quoi par très simple ?
CException est une classe très simple.
C'est une classe polymorphe avec des méthodes virtuelles mais dans ce cas je
lance un objet de la classe de base !

extern std::ostream *error_output;
namespace mtr {

//--------------------------------------------------------------------------------------------------
class CException
//--------------------------------------------------------------------------------------------------
{
private :
std::string _msg;
public :
explicit CException(const std::string& what_arg) : _msg(what_arg)
{}
CException() : _msg("") {}
virtual const std::string& what(void) { return _msg;}
virtual void display(void) { (*error_output) << _msg << std::
endl; }
};

...

} // namespace mtr

on va dire que l'essentiel, c'est d'avoir une version du programme qui
marche....
Merci à toi.


Avatar
Marc G
Une cause possible du problème, évidemment, c'est que son
constructeur de copie n'est pas tout à fait catholique. Mais je
me doute plutôt d'une autre chose : qu'il a compilé g() sans
Support pour les exceptions. VC++, au moins, ne l'active pas par
défaut (au moins, pas dans les anciennes versions---je n'ai pas
essayé de compiler avec la nouvelle sans /EHs). Ce qui
expliquerait pourquoi en ajoutant le catch/throw dans g(), ça
marche -- ça lui oblige d'activer le support des exceptions.


Hélas si !
cette option est activée.
Et en plus, j'ai largement utilisé les exceptions dans d'autres parties du
programme. Avec succès...
Marc

Avatar
Fabien LE LEZ
On Fri, 9 Mar 2007 11:35:35 +0100, "Marc G" :

Lancez par valeur , capturez par référence.


C'est effectivement un bon conseil.
Cf message de James.