IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Exceptions et boule de gomme


Sujet :

C++

  1. #1
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut Exceptions et boule de gomme
    Bonjour à tou(te)s !

    en faisant joujou avec les exceptions sous gcc, je suis tombé sur un comportement des plus étranges...
    Voici un petit bout de code pour illustrer mon propos :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
     
    #include <iostream>
    #include <string>
    using namespace std ;
     
    struct Exception
        {
        Exception ()                                               { cout << "Exception::Exception()" << endl ;}
        Exception ( const Exception & e )     : message(e.message) { cout << "Exception::Exception(e) " << message << endl ;}
        Exception ( const string & m )        : message(m)         { cout << "Exception::Exception(s) " << message << endl ;}
        string message ;
        };
     
    void failure1 ( const string & s )
        {
        cout << "failure1 : just before throw " << s << endl ; 
        throw Exception( s ) ; // throw just constructed Exception : ok
        }
     
    void failure2 ( const string & s )
        {
        static Exception e ;
        e.message = s ;
        cout << "failure2 : just before throw " << s << endl ; 
        throw e ; // throw static Exception : ok
        }
     
    void failure3 ( const string & s )
        {
        Exception e( s ) ;
        cout << "failure3 : just before throw " << s << endl ; 
        throw e ; // throw local Exception : KO
        }
     
    void sub_failure4 ( const Exception & exception )
        {
        cout << "sub_failure4 : just before throw " << exception.message << endl ; 
        throw Exception( exception ) ; // throw just constructed Exception, but from elder one : KO
        }
     
    void failure4 ( const string & message )    
        { 
        cout << "failure4 : just before call sub_failure4" << endl ; 
        sub_failure4( Exception( message )) ;
        }
     
    int main ( int argc , const char ** argv )
        {
        try { throw Exception("Exception 0") ;} catch ( Exception e )  { cout << "catch : " << e.message << endl << endl ;} // ok
        try { failure1("Exception 1" )       ;} catch ( Exception e )  { cout << "catch : " << e.message << endl << endl ;} // ok
        try { failure2("Exception 2" )       ;} catch ( Exception e )  { cout << "catch : " << e.message << endl << endl ;} // ok
        if (argc > 1)
        try { failure3("Exception 3" )       ;} catch ( Exception e )  { cout << "catch : " << e.message << endl << endl ;} // KO !
        try { failure4("Exception 4" )       ;} catch ( Exception e )  { cout << "catch : " << e.message << endl << endl ;} // KO !
        }
    J'ai 5 scénarios de levée d'exception : les 0, 1 et 2 fonctionnent, les 3 et 4 plantent !
    NB : pour pouvoir tester facilement les 3 et 4, je les ai conditionné par la présence d'un argument sur la ligne de commande.

    Compilation (avec gcc (i686-posix-dwarf-rev2, Built by MinGW-W64 project) 4.9.0 sous Windows 7 64bits) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    gcc e.cpp -Wall -fexceptions -O2 -lstdc++ -o e.exe
    Exécution avec l'exception n°4 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    >e.exe
    Exception::Exception(s) Exception 0
    Exception::Exception(e) Exception 0
    catch : Exception 0
     
    failure1 : just before throw Exception 1
    Exception::Exception(s) Exception 1
    Exception::Exception(e) Exception 1
    catch : Exception 1
     
    Exception::Exception()
    failure2 : just before throw Exception 2
    Exception::Exception(e) Exception 2
    Exception::Exception(e) Exception 2
    catch : Exception 2
     
    failure4 : just before call sub_failure4
    Exception::Exception(s) Exception 4
    sub_failure4 : just before throw Exception 4
    Exception::Exception(e) Exception 4
     
    This application has requested the Runtime to terminate it in an unusual way.
    Please contact the application's support team for more information.
    Exécution avec l'exception n°3 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    e.exe x
    Exception::Exception(s) Exception 0
    Exception::Exception(e) Exception 0
    catch : Exception 0
     
    failure1 : just before throw Exception 1
    Exception::Exception(s) Exception 1
    Exception::Exception(e) Exception 1
    catch : Exception 1
     
    Exception::Exception()
    failure2 : just before throw Exception 2
    Exception::Exception(e) Exception 2
    Exception::Exception(e) Exception 2
    catch : Exception 2
     
    Exception::Exception(s) Exception 3
    failure3 : just before throw Exception 3
    Exception::Exception(e) Exception 3
     
    This application has requested the Runtime to terminate it in an unusual way.
    Please contact the application's support team for more information.
    Comme on peut le voir, dans les cas 3 et 4, on n'atteint pas le catch.

    Si quelqu'un(e) comprend quelque chose à ce mystère, je suis preneur !

    Merci d'avance
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 629
    Points : 10 554
    Points
    10 554
    Par défaut
    À vue de pif cela semble logique :

    Exception 3 -> Lancement d'une exception qui est détruite à la fermeture de failure3. Comportement normal pour une variable locale.

    Exception 4 -> Tu dois avoir un problème avec ton constructeur de recopie, puisque ton paramètre doit être détruit à la fermeture de sub_failure4.

  3. #3
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par foetus Voir le message
    À vue de pif cela semble logique :

    Exception 3 -> Lancement d'une exception qui est détruite à la fermeture de failure3. Comportement normal pour une variable locale.
    Mais les exceptions sont censées être lancées par valeur, non?
    (surtout qu'ici en plus, elles sont aussi catchées par valeur)


    Un truc qui serait intéressant serait de rajouter un destructeur à tes exceptions, pour voir quand exactement elles sont détruites.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Membre confirmé Avatar de Gaulouis
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Octobre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 252
    Points : 476
    Points
    476
    Par défaut Pour info
    Salut, j'ai fait un petit test sur Windows 7 32bits(gcc 4.9.1) et sur Ubuntu 64bits (gcc 4.8.4) y'a rien qui plante chez moi

  5. #5
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Pareil que Gaulouis avec gcc et g++ 4.9.1 et 5.0.0 sur Linux.

  6. #6
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut
    Merci les gars !

    Donc si je comprends bien, c'est juste ma config et un léger bug de gcc qui sont en cause...
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  7. #7
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut
    Punaise ! je suis passé à gcc (x86_64-posix-seh-rev2, Built by MinGW-W64 project) 4.9.0 et tout passe !

    Toutefois, je me pose encore des questions à propos de la durée de vie d'une variable levée comme exception...
    J'ai donc codé une variante de mon test précédant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
     
    #include <iostream>
    #include <string>
    using namespace std ;
     
    struct Exception
        {
       ~Exception ()                                                         { cout << "~Exception[" << id << "]()" << endl ;}
        Exception ()                          : id(bid++)                    { cout <<  "Exception[" << id << "]()" << endl ;}
        Exception ( const Exception & e )     : id(bid++),message(e.message) { cout <<  "Exception[" << id << "]([" << e.id << "])" << endl ;}
        Exception ( const string & m )        : id(bid++),message(m)         { cout <<  "Exception[" << id << "]('" << message << "')" << endl ;}
        Exception & set ( const string & m )                                 { message = m ; cout <<  "Exception[" << id << "] = " << message << endl ; return *this ;}
        static int bid ;
        int    id ;
        string message ;
        };
     
    int Exception::bid = 1 ;
     
    void failure1 ( const string & s )
        {
        cout << "failure1 : just before throw " << s << endl ; 
        throw Exception( s ) ;
        }
     
    void failure2 ( const string & s )
        {
        static Exception e ;
        e.set( s ) ;
        cout << "failure2 : just before throw " << s << endl ; 
        throw e ;
        }
     
    void sub_failure3 ( const Exception & exception )
        {
        cout << "sub_failure3 : just before throw " << exception.message << endl ; 
        throw exception ;
        }
     
    void failure3 ( const string & message )    
        { 
        cout << "failure3 : just before call sub_failure3" << endl ; 
        sub_failure3( Exception( message )) ;
        }
     
    int main ()
        {
        try { failure1("***1***" ) ;} catch ( Exception e )          { cout << "catch[" << e.id << "]" << endl ;} ; cout << "---------------------------" << endl ;
        try { failure1("***2***" ) ;} catch ( Exception & e )        { cout << "catch[" << e.id << "]" << endl ;} ; cout << "---------------------------" << endl ;
        try { failure1("***3***" ) ;} catch ( const Exception & e )  { cout << "catch[" << e.id << "]" << endl ;} ; cout << "---------------------------" << endl ;
        try { failure2("***4***" ) ;} catch ( const Exception & e )  { cout << "catch[" << e.id << "]" << endl ;} ; cout << "---------------------------" << endl ;
        try { failure3("***5***" ) ;} catch ( const Exception & e )  { cout << "catch[" << e.id << "]" << endl ;} ; cout << "---------------------------" << endl ;
        }
    et sa sortie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    failure1 : just before throw ***1***
    Exception[1]('***1***')
    Exception[2]([1])
    catch[2]
    ~Exception[2]()
    ~Exception[1]()
    ---------------------------
    failure1 : just before throw ***2***
    Exception[3]('***2***')
    catch[3]
    ~Exception[3]()
    ---------------------------
    failure1 : just before throw ***3***
    Exception[4]('***3***')
    catch[4]
    ~Exception[4]()
    ---------------------------
    Exception[5]()
    Exception[5] = ***4***
    failure2 : just before throw ***4***
    Exception[6]([5])
    catch[6]
    ~Exception[6]()
    ---------------------------
    failure3 : just before call sub_failure3
    Exception[7]('***5***')
    sub_failure3 : just before throw ***5***
    Exception[8]([7])
    ~Exception[7]()
    catch[8]
    ~Exception[8]()
    ---------------------------
    ~Exception[5]()
    De 1,2,3 : on voit qu'on économise une construction par copie en catchant l’exception par référence.
    De 4 : il construit quand même une copie pour éviter de lever une exception statique (on se demande pourquoi...).
    De 5 : il y a une construction par copie en trop selon moi...

    Si quelqu'un a un avis pertinent...
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    À mon avis, la réponse est simple: On ne peut pas faire de throw par référence, seulement par valeur (ou pointeur, façon Java, mais c'est encore autre chose).

    C'est probablement une question de sécurité, car les destructeurs dans les fonctions appelantes sont appelés avant l'entrée dans le bloc catch: ainsi, s'il était possible de lancer une exception par référence, tu pourrais catcher une exception déjà détruite!
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    À mon avis, la réponse est simple: On ne peut pas faire de throw par référence, seulement par valeur (ou pointeur, façon Java, mais c'est encore autre chose).
    Heu... dans les cas 2 et 3, on voit bien que l'exception qui a été construite avant le throw est celle qui arrive jusqu'au catch.
    Donc, d'une manière ou d'une autre, il "sait" que cette instance va être catchée et il n'appelle pas son destructeur.
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Euh, les cas 2 et 3 utilisent tous deux failure1(), qui construit l'exception sur la ligne du throw...

    Edit: En fait en y réfléchissant, c'est un peu comme les appels de fonction: Si une fonction prend un paramètre par valeur mais qu'on construit le paramètre dans l'appel lui-même, celui-ci est construit directement et donc non copié...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  11. #11
    Membre confirmé Avatar de Gaulouis
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Octobre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2015
    Messages : 252
    Points : 476
    Points
    476
    Par défaut Sauf erreur de ma part
    x86_64-posix-seh-rev2 c'est pour une machine 64bit
    or i686-posix-dwarf-rev2 c'est pour une machine 32bit

    fallait essayer avec l'option -arch x86_64

  12. #12
    Provisoirement toléré
    Homme Profil pro
    Architecte réseau
    Inscrit en
    Mars 2016
    Messages
    114
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Architecte réseau
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2016
    Messages : 114
    Points : 0
    Points
    0
    Par défaut
    Dans ces cas là, instrumente tout : constructeurs (y compris constructeur de copie) et destructeur.

Discussions similaires

  1. Question et boule de gomme
    Par said0011 dans le forum Débuter
    Réponses: 1
    Dernier message: 06/11/2007, 19h13
  2. [ActionListener] mystère et boule de gomme
    Par jcodeunpeu dans le forum AWT/Swing
    Réponses: 4
    Dernier message: 23/12/2005, 22h09
  3. Réponses: 3
    Dernier message: 01/11/2002, 14h30
  4. Réponses: 5
    Dernier message: 12/06/2002, 15h12
  5. c: gestion des exceptions
    Par vince_lille dans le forum C
    Réponses: 7
    Dernier message: 05/06/2002, 14h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo