Forum Programmation.c++ Déclencher et capturer une exception

Posté par  .
Étiquettes : aucune
0
25
oct.
2004
Bonjour

En ce moment, je programme une librairie. Elle ne peux donc utiliser std::cerr pour afficher les erreurs, et j'aimerais faire comme j'ai l'habitude en python : déclencher des exceptions.
J'ai essayé ceci, mais ça ne marche pas :
La fonction :

bool rename_file(std::string source, std::string destination) {
std::ifstream flux_in;
std::ofstream flux_out;
std::string ligne;
//fpkg_exception the_exception;
if (!is_file(source)) {
throw "File not found";
return false;
}
return true;
}
C'est volontairement réduit, bien sûr...
Maintenant, j'essaye de l'utiliser :

try {
rename_file("/test", "/bin"));
}
catch (...) {
std::cerr << "Caught exception " << std::endl;
return -1;
}

Ça marche, mais je ne suis pas satisfait. J'aimerais obtenir le texte de l'exception.
J'ai essayé sans succès :
1- catch (std::string e)
2- catch (std::string *e)
3- catch (std::exception e)
3- catch (std::exception *e)
Je pense que ça vient de ma façon de renvoyer l'erreur.
Comment corriger ça ?

Merci d'avance
  • # Oubli

    Posté par  . Évalué à 1.

    J'ai oublié de vous dire : j'ai essayé un truc comme
    fpkg_exception the_exception;
    (d'où le code commenté) avec une exception qui prenne un attribut std::string message mais ça marche pas
    • [^] # Re: Oubli

      Posté par  . Évalué à 2.

      Et si tu faisais tout simplement

      catch (char* exception) {
      std::cerr << exception << std::endl;
      }

      ?
      • [^] # Re: Oubli

        Posté par  . Évalué à 1.

        Non, ça ne marche pas :(
        • [^] # Re: Oubli

          Posté par  . Évalué à 3.

          Et si tu essayais plutôt :

          catch (const char *p) { cerr << p << endl; }

          Je pense que venir du Python vers le C++ demande beaucoup plus d'effort que cela en a l'air. Le C++ met en place un framework objet autour du C mais nous permet encore d'accéder, tel le C, au produit de notre code. Ce n'est pas une interface d'abstraction (et c'est tant mieux comme çà). Cela redemande de savoir comment fonctionne un ordinateur, ce qui se perd un peu de nos jours. Donc:

          - "Chaine", en C comme en C++, se réfère à un bloc de texte codé en dur dans ton programme. Tu obtiens donc un pointeur vers une chaîne qui se trouve en mémoire read-only. C'est donc une expression de type « const char * ». Si le C est assez lâche, le C++ est beaucoup plus strict en ce qui concerne le typage.

          - string, ou plus précisément std::string, n'est pas du tout un type natif mais un objet d'une bibliothèque bien définie, la STL. C'est donc une classe évoluée, qui n'a rien à voir avec un type natif tel qu'un pointeur (lequel étant implicitement défini par l'usage des guillemets).

          - C++ est suffisament versatile pour te permettre de lancer n'importe quoi, parce que cela colle au paradigme objet (d'autres langages peuvent t'imposer des restrictions tel que le type d'objet à lancer), mais si tu tiens à utiliser la STL, autant le faire jusqu'au bout en dérivant la classe « exception » et en redéfinissant la méthode what() (qui renvoie un const char *, d'ailleurs). Ensuite tu rattrapes une seule fois « exception », et tu prendras dans tes filets tout ce qui en dérive.

          - Pourquoi colles-tu « return false; » dans ton if si tu lances une exception à la ligne d'avant ? Celle-ci, par définition, ne reviendra pas.

          - Lorsque j'écris une bibliothèque, j'utilise également les noms pleinement qualifiés dans un souci de rectitude. Mais si tu comptes utiliser souvent les objets de la STL, je te conseille de placer « using namespace std; » en tête de ton listing. Cela t'épargnera l'usage des « std:: » à chaque coin de ligne et te fera gagner énormément en temps et en lisibilité.
          • [^] # Re: Oubli

            Posté par  . Évalué à 2.

            pour les chaînes : j'adore l'approche de la STL avec son objet std::string, c'est très pratique...
            (tu ne m'apprends rien sur ça)
            si tu tiens à utiliser la STL, autant le faire jusqu'au bout en dérivant la classe « exception » et en redéfinissant la méthode what() (qui renvoie un const char *, d'ailleurs).
            J'avais essayé, mais je m'y prenais mal: j'avais pas bien compris ce qu'il fallait surcharger !!
            Pourquoi colles-tu « return false; »
            Parce que c'était copié/collé d'un code précédent, sans exception, et que je l'avais pas nettoyé
            noms pleinement qualifiés
            Que veux tu dire ?
            M'enfin, c'est une lib à usage plutôt "interne" à plusieurs applis d'un projet
            • [^] # Re: Oubli

              Posté par  . Évalué à 3.

              (tu ne m'apprends rien sur ça)

              Ah, désolé. Mon post était purement informatif, je ne te prenais pas de haut.

              Que veux tu dire ?
              Utiliser le nom de l'objet avec celui de son namespace devant, en utilisant l'opérateur de portée « :: ».

              M'enfin, c'est une lib à usage plutôt "interne" à plusieurs applis d'un projet

              Et c'est très bien ! Mais justement, pour le coup, cela vaut le coup de la chiader un poil. En général, les listings des mes applications sont écrits au rythme de croisière normal (un minimum de commentaires, des fonctions implémentant ce dont j'ai besoin et rien d'autre), quand mes bibliothèques tentent, autant qu'elles le peuvent, de faire figure d'exemple :Toutes les indentations qui vont bien, tous les objets déclarés et initialisés proprement, un souci d'exhaustivité (si je fais une classe Rond, j'en profite pour faire la classe Carré à coté, et au final développer tout un toolkit de gestion de formes), un commentaire pratiquement à chaque ligne, disposés à la mode « assembleur » (le code à gauche, les commentaires à droite), etc. Et également :Une suite de tests. C'est probablement la partie la plus pénible à écrire, mais ô combien utile. On détecte toujours trois à quatre fois plus de bugs que l'on ne l'aurait estimé.

              Certes, je mets beaucoup plus de temps à écrire une lib qu'une simple application, mais c'est un investissement car elle n'est en général plus à faire par la suite, et surtout, il ne manque plus grand chose à la fin pour pouvoir être diffusée dans le public comme projet open-source.
              • [^] # Re: Oubli

                Posté par  . Évalué à 1.

                Ah, désolé.
                Ce n'est rien. J'ai de grosses lacunes en C++ parce que j'ai appris avec un bouquin qui ne "connaît" pas la STL, les namespace, les templates, les exceptions...
                Utiliser le nom de l'objet avec celui de son namespace devant
                J'ai pas d'objet dans cette partie là pour l'instant : c'est les fonctions spéciales qui servent à tout et n'importe quoi, et "encapsulent" les appels à des fonctions C
                D'ailleurs, tu ne connais pas un remplaçant pour l'appel system qui ne lance pas /bin/sh ?
                au final développer tout un toolkit
                Toi aussi ?
                J'avais commencé une fonction encapsulant les recherches XPath dans libxml2, j'ai presque une libxml2 complète (DOM uniquement) qui utilise le C++, sans objet par contre (Ça devrait arriver)
                Une suite de tests
                Mais mon code est parfait ! :p
                Sans déc, je teste le code en lançant l'appli, tout simplement : pas de code inutile !
                • [^] # Re: Oubli

                  Posté par  . Évalué à 2.

                  Ce n'est rien. J'ai de grosses lacunes en C++ parce que j'ai appris avec un bouquin qui ne "connaît" pas la STL, les namespace, les templates, les exceptions...

                  Hmm, un bouquin de C++ qui ne connaît pas les exceptions ? Pas génial, en effet. Moi personnellement, je ne jure que par le cours de Christian Casteyde : http://casteyde.christian.free.fr(...)

                  Sans déc, je teste le code en lançant l'appli, tout simplement : pas de code inutile !

                  Bah tu fais ce que tu veux, mais d'expérience, je te dirais que ce n'est pas suffisant. Evidement, il ne faut pas écrire la suite de tests à la va-vite. Il faut être sûr qu'elle-même va éprouver la classe concernée dans tous les cas de figure possibles. Je suis presque sûr que tu vas trouver quatre ou cinq bugs là où tu étais prêt à garantir qu'il n'y en avait pas un seul.

                  C'est particulièrement important lorsque tu écris les premières classes de ta bibliothèque parce que celles-ci seront dérivées et que le bug concerné va se transmettre à leurs descendants.
          • [^] # Re: Oubli

            Posté par  (site web personnel) . Évalué à 3.

            C++ est suffisament versatile

            Attention, faux-ami. Tu veux dire polyvalent. "En français, versatile qualifie une personne qui change souvent d'opinion ou de parti, qui est inconstante ou lunatique", pour citer le Grand Dictionnaire Terminologique.

            http://www.granddictionnaire.com/btml/fra/r_motclef/index1024_1.asp(...)
  • # Ça marche

    Posté par  . Évalué à 1.

    Il suffit de faire throw std::string("File not found"); et catch (std::string e);
    Merci à Florent qui m'a mis sur la piste..
    • [^] # Re: Ça marche

      Posté par  (site web personnel) . Évalué à 3.

      En toute rigueur, il faut faire "catch (std::string &e);", sans quoi l'objet servant d'exception (ici un std::string) va se retrouver copie. Sinon, il me semblerait plus judicieux d'utiliser les exceptions standard. Il en existe une bonne douzaine répartis suivant les types d'erreur que peut rencontrer un programme. Dans ton cas, j'ecrirai surement :
      bool rename_file(std::string source, std::string destination) {
      std::ifstream flux_in;
      std::ofstream flux_out;
      std::string ligne;
      //fpkg_exception the_exception;
      if (!is_file(source)) {
        throw std::invalid_argument("File not found");
      }
      return true;
      }
      
      try {
      rename_file("/test", "/bin"));
      }
      catch (std::exception &e) {
      std::cerr << "Caught exception: " << e.what() << std::endl;
      return -1;
      }
      
      • [^] # Re: Ça marche

        Posté par  . Évalué à 1.

        les exceptions standard.
        Où en trouver la liste ?
        Merci d'avance
        (j'ai regardé /usr/include/g++/exception, mais c'est pas là :(
        • [^] # Re: Ça marche

          Posté par  . Évalué à 1.

          J'ai trouvé ça dans /usr/include/g++/stdexcept
          Y'en a pas des masses :
          class logic_error : public exception
          class domain_error : public logic_error
          class invalid_argument : public logic_error
          class length_error : public logic_error
          class out_of_range : public logic_error
          class runtime_error : public exception
          class range_error : public runtime_error
          class overflow_error : public runtime_error
          class underflow_error : public runtime_error
          J'aurais aimé des file_error par exemple...
          M'en vais en définir pour moi...
        • [^] # Re: Ça marche

          Posté par  . Évalué à 1.

          Petit tutorial sur les exceptions, avec la hiérarchie de la bibliothèque standard :
          http://www.cplusplus.com/doc/tutorial/tut5-3.html(...)
    • [^] # Re: Ça marche

      Posté par  . Évalué à 2.

      Ca marche, mais cela ne te dit ni pourquoi ça marche, ni pourquoi ton précédent hack ne marchait pas.

      C'est un défaut assez grave dont souffrent beaucoup de stagiaires qui passent chez nous. On tâtonne jusqu'à ce que ça marche, mais sans pouvoir expliquer ses choix. Du coup, rien ne garantit que l'on a réglé le problème, ni permet d'affirmer qu'il ne se représentera pas. Pire : Cela peut déclencher des conflits avec d'autres modules. L'exemple-type est cet extrait tiré des fortunes de la tribune :

      16:39 on m'a demander de debugger un prog C qui marche pas, pour faire
      simple j'ai remplacé tous les malloc( x) par des mallox( x * 100 )
      ... et bien maintenant ça marche ... aller Zou ! au suivant !


      Il est effrayant de voir combien les gens qui font réellement ce genre de chose dans leur métier sont nombreux ...
      • [^] # Re: Ça marche

        Posté par  . Évalué à 1.

        J'avais compris que je ne renvoyais pas le bon type, et que par conséquent je ne pouvais le capturer.
        Le raisonnement pour trouver le pb :
        1- essayer un catch (...) => ça capturait donc je ne me trompais pas dans l'émission
        2- essayer de capturer avec std::string : ça ne marchait pas
        En fait, j'avais oublié qu'il fallait mettre std::string autour d'une chaîne...
        • [^] # Re: Ça marche

          Posté par  . Évalué à 2.

          En fait, j'avais oublié qu'il fallait mettre std::string autour d'une chaîne...

          C'est cette manière de présenter la chose qui me perturbe un peu.

          Cela s'explique parce que tu indiques plus haut que tu souhaites travailler en STL avec l'objet string, mais ce n'est pas une condition requise par le C++. les chaînes de caractères (type C) et les string de la STL sont réellement deux choses complètement différentes. D'ailleurs créer une string juste pour la « durée de vol » de l'exception est un gâchis de ressources.

          Présenté comme cela, ça ressemble à une espèce d'adaptateur pour faire fonctionner du code Python en C++. A la place, autant prendre directement les bonnes habitudes ...

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.