8
votes

Usage correct de std :: non apprenant_exception dans un destructeur

Il y a quelques articles de conclusion "ne jetez jamais une exception d'un destructeur", et "std :: non-celles-ci () n'est pas utile", par exemple:

  • http://www.gotw.ca/gotw/047.htm ( Écrit par Herb Sutter) Li> ul>

    mais il semble que je ne reçois pas le point. Alors j'ai écrit un petit exemple de test (voir ci-dessous). P>

    Étant donné que tout va bien avec l'exemple de test, j'apprécierais très que certains commentaires concernant ce qui pourrait se tromper? P>

    Résultats du test : P>

    ./ Main P>

    // file main.cpp
    // build with e.g. "make main"
    // tested successfully on Ubuntu-Karmic with g++ v4.4.1
    #include <iostream>
    
    class Foo {
      public:
    
      int bar(int i) {
        if (0 == i)
          throw(std::string("from ") + __PRETTY_FUNCTION__);
        else
          return i+1;
      }
    
      ~Foo() {
        bool exc_pending=std::uncaught_exception();
        try {
          bar(0);
        } catch (const std::string &e) {
          // ensure that no new exception has been created in the meantime
          if (std::uncaught_exception()) exc_pending = true;
    
          if (exc_pending) {
            std::cerr << __PRETTY_FUNCTION__ 
                      << ": caught exception - but have pending exception - ignoring"
                      << std::endl;
          } else {
            std::cerr << __PRETTY_FUNCTION__
                      << ": caught exception -  but *no* exception is pending - rethrowing"
                      << std::endl;
            throw(std::string("from ") + __PRETTY_FUNCTION__);
          }
        }
      }
    
    };
    
    int main(int argc, char** argv) {
      try {
        Foo f;
        // will throw an exception in Foo::bar() if no arguments given. Otherwise
        // an exception from Foo::~Foo() is thrown.
        f.bar(argc-1);
      } catch (const std::string &e) {
        std::cerr << __PRETTY_FUNCTION__ << ": caught exception: " << e << std::endl;
      }
      return 0;
    }
    


3 commentaires

Quelle est ta question? Vous semblez observer le comportement attendu d'un mécanisme que vous ne devriez pas utiliser. Considérez-vous l'utiliser en dehors d'un contexte de test / expérimentation, et pourquoi?


@Potatoswatter: La question est la suivante: malgré les avertissements dans certains articles, cela fonctionne comme prévu - alors ce qui pourrait se tromper?


Nuff juste, mais cela ne cite pas ce que vous avez dit, et il est impossible de savoir ce qui est attendu, approprié, ou sûr dans votre contexte sans plus d'informations. L'itinéraire sécurisé, en tant que balayage d'herbes et beaucoup d'autres vous diront, ne veut pas jeter d'un destructeur en premier lieu.


3 Réponses :


9
votes

Sutter Herb fait référence à un problème différent. Il parle de:

try
{
}
catch (...)
{
    try
    {
        // here, std::uncaught_exception() will return true
        // but it is still safe to throw an exception because
        // we have opened a new try block
    }
    catch (...)
    {
    }
}


2 commentaires

@Samuel: ... se référant à une autre IssSue ... c'est ce que j'ai aussi pensé. Et - puisque je ne jette pas une exception quand3Ecave_Exception () renvoie vrai - croyez-vous que mon approche est un coffre-fort?


Ce n'est pas vrai. Votre exception est prise à ce stade. Vous auriez dû mettre l'essai / attraper dans un destructeur, qui est invoqué pendant la pile de déroulement (en raison d'une exception active). Dans cette situation, une exception est activement déroulant la pile, mais vous avez raison puis à l'intérieur d'un bloc d'essai / attraper où vous peut lancer une exception.



6
votes

Il n'y a rien de tecourci techniquement avec votre code. Il est parfaitement sûr que vous ne terminerez jamais accidentellement parce que vous avez lancé une exception quand il n'était pas sûr. La question est que ce n'est pas utile, en ce sens qu'elle ne jettera pas parfois pas une exception quand elle est prudente. La documentation de votre destructeur doit fondamentalement dire «cela pourrait ou pourrait ne pas jeter une exception.»

S'il ne jettera pas d'exception occasionnellement, Vous ne pouvez aussi jamais jeter une exception . De cette façon, vous êtes au moins cohérent.


0 commentaires

1
votes

Sutter Herb parle de la situation lorsqu'un objet de classe t code> est détruit alors qu'il existe une exception non capturée dans un objet de classe u code>. STD :: non-celles_Exception () code> retournerait true code> dans le "code> t code> destructeur. Le destructeur serait incapable de déterminer si elle s'appelle pendant la pile déroulant. Si c'est le cas, il ne faut pas jeter, sinon c'est des affaires comme d'habitude.

La classe u code> aurait un problème à l'aide de la classe t code> dans le destructeur. u code> se trouverait traiter avec un objet inutile t code> qui refuserait de faire quoi que ce soit risqué dans son destructeur (pouvant inclure écrire un fichier journal ou commettraliser une transaction dans une base de données) . P>

Sutter d'herbes suggère de ne jamais jeter dans un destructeur, ce qui est une bonne idée. Cependant, le C ++ 17 offre une autre option. Il introduit std :: non apprenat_exceptions () code>, qui peut être utilisé pour déterminer si le destructeur peut lancer. Exemple suivant montre le problème s'il est respecté en mode C ++ 14. Si compilé en mode C ++ 17, cela fonctionnerait correctement. P>

#include <exception>
#include <iostream>
#include <string>

class T
{
  public:

    ~T() noexcept(false)
    {
#if __cplusplus >= 201703L
      // C++17 - correct check
      if (std::uncaught_exceptions() == uncaught_exceptions_)
#else
      // Older C++ - incorrect check
      if (!std::uncaught_exception())
#endif
      {
        throw (std::string{__PRETTY_FUNCTION__} + " doing real work");
      }
      else
      {
        std::cerr << __PRETTY_FUNCTION__ << " cowardly quitting\n";
      }
    }

  private:

#if __cplusplus >= 201703L
    const int uncaught_exceptions_ {std::uncaught_exceptions()};
#endif
};

class U
{
  public:

    ~U()
    {
      try
      {
        T t;
      }
      catch (const std::string &e)
      {
        std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
      }
    }
};

int main()
{
  try
  {
    U u;
    throw (std::string{__PRETTY_FUNCTION__} + " threw an exception");
  }
  catch (const std::string &e)
  {
    std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
  }
  return 0;
}


0 commentaires