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

Boost C++ Discussion :

try multiple catch silencieux


Sujet :

Boost C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut try multiple catch silencieux
    Bonjour à tous,

    Je sais pas si je poste au bon endroit, dites moi si je dois déplacer le message. Voici mon problème: j'ai un bloc d'instructions qui peut lever un ensemble d'exceptions qui n'ont pas forcément de relation d'héritage que je souhaiterait catcher, un truc dans ce goût là:
    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
     
    struct exception1 : std::exception {};
    struct exception2 : std::exception {};
    struct exception3 : std::exception {};
     
    void une_fonction()
    {
        //... des instructions
     
        typedef vector<exception1, exception2> exception_pool_1;
        try {
            //.. des instructions pouvant lever des exceptions de type de exception_pool_1
        }
        catch(/*une ou plusieurs exceptions de exception_pool_1*/) {
            //rien ou un appel compilant pour tout type de pool_1    
        }
     
        //... des instructions
     
        typedef vector<exception1, exception3> exception_pool_2;
        //... des instructions
        try {
            //.. des instructions pouvant lever des exceptions de type de exception_pool_2
        }
        catch(/*une ou plusieurs exceptions de exception_pool_2*/) {
            //rien ou un appel compilant pour tout type de pool_2    
        }
    }
    Je ne sais pas si c'est possible de faire quelque chose avec boost, je suis tombé sur ce site qui parle en gros du même problème mais ils proposent une solution à base de macro. Dans cette discussion, un gars parle d'une solution à base de boost fusion, mais j'avoue ne pas du tout voir comment faire, si vous avez des idées...

  2. #2
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    août 2003
    Messages
    5 272
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : août 2003
    Messages : 5 272
    Points : 10 962
    Points
    10 962
    Par défaut
    J'avais eu l'occasion de faire un truc à base de macros et de typelists.
    Je partais de la constatation que mes blocs catch avaient toujours le même schéma commun:
    - log avec paramètres, dont des infos provenant de l'exception
    - éventuelle émission de notification corba.
    - transformation de l'exception -> réémission, absorption, ou transformation en une autre exception.

    Au final, je ne suis pas sûr que mon usine à gaz fusse la meilleure idée que j'ai pu avoir.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    Bon alors finalement j'ai fait un truc dans ce goût là (mélange de macro et de typelist):
    La partie qui va gérer les différents catchs:
    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
     
    #include <iostream>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/front.hpp>
    #include <boost/mpl/pop_front.hpp>
    #include <boost/mpl/empty.hpp>
     
    using namespace boost::mpl;
     
    template<typename v, bool empty>
    struct error_handler_impl
    {};
     
    template<typename v>
    struct error_handler_impl<v, false>
    {
      static void apply()
      {
        try {
          throw;
        } catch(front<v>::type const& e) {
          std::cerr << "exception silencieuse " << e.what() << std::endl;
        } catch(...) {
          typedef typename pop_front<v>::type next;
          typedef typename empty<next>::type empty;
          error_handler_impl<next, empty::value>::apply();
        }
      }
    };
     
    template<typename v>
    struct error_handler_impl<v, true>
    {
      static void apply()
      {
        throw;
      }
    };
     
    template<typename v>
    void error_handler()
    {
      typedef typename empty<v>::type empty;
      error_handler_impl<v, empty::value>::apply();
    }
    La partie macro pour alléger un peu les écritures:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #define silent_try try{
    #define silent_catch(exception_pool) } catch(...) { \
      error_handler<exception_pool>(); \
    } \
    avec un exemple d'utilisation:
    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
     
    #include <exception>
     
    struct exception1 : std::exception
    {
      virtual char const* what() const throw() { return "exception1"; }
    };
     
    struct exception2 : std::exception
    {
      virtual char const* what() const throw() { return "exception2"; }
    };
     
    struct exception3 : std::exception
    {
      virtual char const* what() const throw() { return "exception3"; }
    };
     
    void f1() { throw exception1(); }
     
    void f2() { throw exception2(); }
     
    void f3() { throw exception3(); }
     
    int main()
    {
      typedef vector<exception1, exception2> exception_pool_1;
      silent_try
        f1();
      silent_catch(exception_pool_1);
     
      silent_try
        f2();
      silent_catch(exception_pool_1);
     
      try {
        silent_try
          f3();
        silent_catch(exception_pool_1);
      }
      catch(std::exception const& e)
      {
        std::cout << "exception non catchée " << e.what() << std::endl;
      }
      return 0;
    }
    Ce qui donne en sortie:
    exception silencieuse exception1
    exception silencieuse exception2
    exception non catchée exception3
    J'ajouterai sans doute un paramètre template supplémentaire pour pouvoir paramétrer le comportement à adopter vis-à-vis d'un pool d'exceptions (en gros pouvoir paramétrer la ligne std::cerr << "exception silencieuse " << e.what() << std::endl; de la structure error_handler_impl) mais dans les grandes lignes ça fait bien ce que je voulais...

  4. #4
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Jolie solution !

    Au passage, pourquoi ne pas simplement supprimer la ligne affichant sur stderr, pour simplement utiliser comme ça (sans silent_[try|catch]) :
    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
    #include <exception>
     
    struct exception1 : std::exception
    {
      virtual char const* what() const throw() { return "exception1"; }
    };
     
    struct exception2 : std::exception
    {
      virtual char const* what() const throw() { return "exception2"; }
    };
     
    struct exception3 : std::exception
    {
      virtual char const* what() const throw() { return "exception3"; }
    };
     
    void f1() { throw exception1(); }
     
    void f2() { throw exception2(); }
     
    void f3() { throw exception3(); }
     
    int main()
    {
      typedef vector<exception1, exception2> exception_pool_1;
      try {
        f1();
      } catch(...) {
        error_handler<exception_pool_1>(); // Auto re-throw if no match
        std::cerr << "Exception silencieuse sur f1" << std::endl;
      }
     
      try {
        f2();
      } catch(...) {
        error_handler<exception_pool_1>();
        // Do nothing : just ignore
      }
     
      try {
        try {
          f3();
        } catch(...) {
          error_handler<exception_pool_1>();
          std::cerr << "Exception pool 1 on f3" << std::endl;
        }
      }
      catch(std::exception const& e)
      {
        std::cout << "exception non catchée " << e.what() << std::endl;
      }
      return 0;
    }
    Ou bien juste déterminer try / catch autrement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #define catch_pool(exception_pool) \
      catch(...) { \
        error_handler<exception_pool>(); \
        do \
    #define catch_pool_end \
        while (false);
      }
    Et utilisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    try {
      f1();
    } catch_pool(exception_pool_1) {
      std::cerr << "exception_pool_1 on f1" << std::endl;
    } catch_pool_end
    (Disclaimer : Aucun code de ce message n'a été testé !)

  5. #5
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    août 2003
    Messages
    5 272
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : août 2003
    Messages : 5 272
    Points : 10 962
    Points
    10 962
    Par défaut
    Je sortirai les accolades des macros.
    Mieux vaut forcer l'utilisateur à les saisirs, ainsi les IDE sauront indenter automatiquement.

    Sinon, j'ai retrouvé ce vieux fil
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    @Ekleog : le problème de faire comme tu le propose c'est que potentiellement je voudrais associer un traitement aux exceptions du pool (et pas seulement logger "j'ai catché une des exceptions du pool mais je sais pas laquelle ;-)" )
    Là où je n'ai peut être pas été assez clair, c'est que mes méthodes peuvent ou non lever une exception, en pratique j'exécute donc plusieurs appels successifs avant de catcher un pool d'exception (j'ai mis un exemple plus bas).

    J'ai ajouté un paramètre template et utilisé les variadic macro. J'ai pas le code sous la main mais de tête ça donne à peu près ça:
    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
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/front.hpp>
    #include <boost/mpl/pop_front.hpp>
     
    using namespace boost::mpl;
     
    template<typename v, typename function_type>
    struct error_handler
    {
      static void apply(function_typeconst& f)
      {
        try {
          throw;
        } catch(front<v>::type const& e) {
          f(e);
        } catch(...) {
          error_handler<pop_front<v>::type>::apply(f);
        }
      }
    };
     
    template<typename function_type>
    struct error_handler< vector0<> >
    {
      static void apply(function_type const&)
      {
        throw;
      }
    };
    avec la macro associée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #define multi_catch(f, ...) \
    catch(...) { \
      error_handler<__VA_ARGS__>::apply(f); \
    } \
    avec l'utilisation:
    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
    struct LogPolicy
    {
      template<typename T>
      void operator()(T const& e) const
      {
        std::clog << e.what() << std::endl;
      }
    };
     
    int main()
    {
      try {
        f1();  //peu ou pas lever une exception
        f2();  //peu ou pas lever une exception
      } multi_catch(LogPolicy(), exception1, exception2);
     
      try {
        try {
          f3();
        } multi_catch(LogPolicy(), exception1, exception2);
      }
      catch(std::exception const& e)
      {
        std::cout << "exception non catchée " << e.what() << std::endl;
      }
      return 0;
    }
    L'avantage de cette approche (pour mon utilisation) est qu'en modifiant LogPolicy je peux spécialiser l'operator() pour certains types d'exception (on peut même imaginer une structure composant plusieurs politiques par type d'exception avec un traitement par défaut)

    @Luc : je veux bien sortir les accolades pour le try (cf. ci dessus) mais pour le catch, je vois pas trop comment faire. Je vais prendre le temps de regarder le thread que tu m'as conseillé, j'ai l'impression que tes problématiques étaient très proche de la mienne.

  7. #7
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Tu as sans doute mal compris ce que je voulais donner comme code.

    Je donne un exemple complet :
    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
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    #include <iostream>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/front.hpp>
    #include <boost/mpl/pop_front.hpp>
    #include <boost/mpl/empty.hpp>
     
    using namespace boost::mpl;
     
    template<typename v, bool empty>
    struct error_handler_impl
    {};
     
    template<typename v>
    struct error_handler_impl<v, false>
    {
      static void apply()
      {
        try {
          throw;
        } catch(typename front<v>::type const& e) {
        } catch(...) {
          typedef typename pop_front<v>::type next;
          typedef typename empty<next>::type empty;
          error_handler_impl<next, empty::value>::apply();
        }
      }
    };
     
    template<typename v>
    struct error_handler_impl<v, true>
    {
      static void apply()
      {
        throw;
      }
    };
     
    template<typename v>
    void error_handler()
    {
      typedef typename empty<v>::type empty;
      error_handler_impl<v, empty::value>::apply();
    }
     
    #include <exception>
     
    struct exception1 : std::exception
    {
      virtual char const* what() const throw() { return "exception1"; }
    };
     
    struct exception2 : std::exception
    {
      virtual char const* what() const throw() { return "exception2"; }
    };
     
    struct exception3 : std::exception
    {
      virtual char const* what() const throw() { return "exception3"; }
    };
     
    void f1() { throw exception1(); }
     
    void f2() { throw exception2(); }
     
    void f3() { throw exception3(); }
     
    void foo() {
      std::cout << "foo" << std::endl;
    }
     
    int main()
    {
      typedef vector<exception1, exception2> exception_pool_1;
      try {
        f1();
      } catch(...) {
        error_handler<exception_pool_1>(); // Auto re-throw if no match
        std::cerr << "Exception silencieuse sur f1" << std::endl;
      }
     
      try {
        f2();
      } catch(...) {
        error_handler<exception_pool_1>();
        // Do nothing : just ignore
      }
     
      try {
        try {
          f3();
        } catch(...) {
          error_handler<exception_pool_1>();
          std::cerr << "Exception pool 1 on f3" << std::endl;
        }
      }
      catch(std::exception const& e)
      {
        std::cout << "exception non catchée " << e.what() << std::endl;
      }
     
      try {
        f2();
      } catch(...) {
        error_handler<exception_pool_1>();
        foo();
      }
      return 0;
    }
    La sortie :
    Exception silencieuse sur f1
    exception non catchée exception3
    foo
    Ce qui te permet de faire comme tu veux pour le traitement des exceptions !

    Le truc, c'est que error_handler<exception_pool>() va re-throw si l'exception n'est pas dans exception_pool ; et ne va rien faire dans le cas contraire. Ce qui fait qu'après cette ligne, on est certain que l'exception est dans exception_pool. Mais, si l'exception n'est pas dans exception_pool, alors on va sortir du catch(...) en relançant l'exception : elle n'est pas gérée.

    Dans le cas de plusieurs exception_pool à enchaîner, il est toujours possible de mettre tout le try {} catch(...) {} dans un autre try {} catch(...).

    Enfin, il y a sûrement moyen de placer des macros pour ajouter du sucre syntaxique !

  8. #8
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Je viens de mettre sur mon dépôt une version adaptée à ma sauce (avec macros syntaxiques et tout) d'une gestion d'exceptions.

    cf. https://github.com/Ekleog/misc (exception_pool_mpl.cpp principalement)

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    En fait c'est sur cette partie là que l'on s'est mal compris (et je pense que le nom initial de ma macro silent_catch n'a pas du tout aidé):
    Le truc, c'est que error_handler<exception_pool>() va re-throw si l'exception n'est pas dans exception_pool ; et ne va rien faire dans le cas contraire.
    En fait par rapport à mon premier post:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        typedef vector<exception1, exception2> exception_pool_1;
        try {
            //.. des instructions pouvant lever des exceptions de type de exception_pool_1
        }
        catch(/*une ou plusieurs exceptions de exception_pool_1*/) {
            //rien ou un appel compilant pour tout type de pool_1   
        }
    Avec ta proposition on fait bien la première partie de ma "spec", i.e. "rien" ou une sous partie de "un appel compilant pour tout type de pool_1" (dans ton cas, uniquement des appels ne dépendants pas de l'exception levée), idéalement, je voudrais pouvoir écrire un truc comme ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        typedef vector<exception1, exception2> exception_pool_1;
        try {
            //.. des instructions pouvant lever des exceptions de type de exception_pool_1
        }
        catch(auto const& e in exception_pool_1) {
            e.what();
            e.to_error_code(); //ou tout autre instruction pouvant compiler pour tout type de exception_pool_1
        }
    Dans ma solution, je m'en sort en déportant la partie e.what(); et e.to_error_code(); dans un foncteur templaté sur son operator(), ça a l'avantage de répondre à ma "spec" initiale mais par contre ça à l'inconvénient des foncteurs (i.e. de délocaliser le code par rapport au site d'appel)

    PS: le code correspondant dans ma version:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct Function
    {
      template<typename ExceptionType>
      void operator()(ExceptionType const& e) const
      {
        e.what();
        e.to_error_code();
      }
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        try {
            //.. des instructions pouvant lever des exceptions de type de exception_pool_1
        } multi_catch(Function(), exception1, exception2);

  10. #10
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    C'est bon !

    J'ai réussi à faire ce que tu souhaites en macros (hello boost preprocessor).

    C'est disponible sous GPL ici : https://github.com/Ekleog/misc/blob/...ption_pool.cpp

    A noter : je t'accorde l'utilisation de cette solution, si tu le désires, sans les contraintes de la GPL. (Oui, tu m'as bien donné l'idée, je ne vais pas te priver du résultat en collant le tout sous GPL sans te donner d'exception - même si le code a été long à trouver !)

Discussions similaires

  1. Réponses: 6
    Dernier message: 05/05/2007, 11h12
  2. Formulaire tri multiple
    Par sheeridan dans le forum IHM
    Réponses: 2
    Dernier message: 26/12/2006, 07h10
  3. Tri multiple (programmeur Perl pas doué inside)
    Par Arioch dans le forum Langage
    Réponses: 5
    Dernier message: 18/07/2006, 12h47
  4. UTILISATION DE TRY et CATCH
    Par demcoul dans le forum JBuilder
    Réponses: 1
    Dernier message: 15/04/2006, 15h01
  5. Pb : Exception / déroutements / try, throw, catch
    Par Bapt_from_Reims dans le forum C++
    Réponses: 5
    Dernier message: 18/03/2005, 17h55

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