Throw from a destructor

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Phlip

    Throw from a destructor

    C++ers:

    I have this friend who thinks C++ cannot throw from a destructor.

    I think throwing from a destructor is bad Karma, and should be designed
    around, but that C++ cannot strictly prevent the actual event.

    (One compiler-oriented reason is a compiler might not be able to detect
    that the call-tree from a destructor leads to a throw.)

    Put another way, suppose I have a big object, aBigSimCity, and it has a
    ..shutdown() method. It must call before ~BigSimCity() calls, and it might
    throw. This implies my Wrapper class, containing aBigSimCity, should never
    declare a destructor like this: ~Wrapper() { aBigSimCity.shu tdown(); }.

    ..shutdown() might throw, and I don't feel like wrapping it in
    try{}catch(). ~Wrapper should not throw, hence I need a better system to
    break things down.

    So which of us is right?

    --
    Phlip
  • Phlip

    #2
    Re: Throw from a destructor

    Phlip wrote:
    I have this friend who thinks C++ cannot throw from a destructor.
    >
    I think throwing from a destructor is bad Karma, and should be designed
    around, but that C++ cannot strictly prevent the actual event.
    >
    (One compiler-oriented reason is a compiler might not be able to detect
    that the call-tree from a destructor leads to a throw.)
    >
    Put another way, suppose I have a big object, aBigSimCity, and it has a
    .shutdown() method. It must call before ~BigSimCity() calls, and it might
    throw. This implies my Wrapper class, containing aBigSimCity, should never
    declare a destructor like this: ~Wrapper() { aBigSimCity.shu tdown(); }.
    >
    .shutdown() might throw, and I don't feel like wrapping it in
    try{}catch(). ~Wrapper should not throw, hence I need a better system to
    break things down.
    Forgot to mention why this isn't a FAQ:



    Suppose the Wrapper object contains a catch(...) that takes care of any
    exceptions. So in this specific case, the Wrapper's destructor can
    never be called during un-winding from a thrown exception, so its
    destructor can always safely call things that might throw.

    Don't do it anyway.

    --
    Phlip

    Comment

    • Ian Collins

      #3
      Re: Throw from a destructor

      Phlip wrote:
      C++ers:
      >
      I have this friend who thinks C++ cannot throw from a destructor.
      >
      I think throwing from a destructor is bad Karma, and should be designed
      around, but that C++ cannot strictly prevent the actual event.
      >
      (One compiler-oriented reason is a compiler might not be able to detect
      that the call-tree from a destructor leads to a throw.)
      >
      Put another way, suppose I have a big object, aBigSimCity, and it has a
      ..shutdown() method. It must call before ~BigSimCity() calls, and it might
      throw. This implies my Wrapper class, containing aBigSimCity, should never
      declare a destructor like this: ~Wrapper() { aBigSimCity.shu tdown(); }.
      >
      ..shutdown() might throw, and I don't feel like wrapping it in
      try{}catch(). ~Wrapper should not throw, hence I need a better system to
      break things down.
      >
      So which of us is right?
      >
      You!

      I tend to add a throw() to my destructors (especially virtual) to
      indicate that they will not throw.

      --
      Ian Collins.

      Comment

      • Joe Van Dyk

        #4
        Re: Throw from a destructor

        Phlip wrote:
        C++ers:
        >
        I have this friend who thinks C++ cannot throw from a destructor.
        >
        I think throwing from a destructor is bad Karma, and should be designed
        around, but that C++ cannot strictly prevent the actual event.
        >
        (One compiler-oriented reason is a compiler might not be able to detect
        that the call-tree from a destructor leads to a throw.)
        >
        Put another way, suppose I have a big object, aBigSimCity, and it has a
        .shutdown() method. It must call before ~BigSimCity() calls, and it might
        throw. This implies my Wrapper class, containing aBigSimCity, should never
        declare a destructor like this: ~Wrapper() { aBigSimCity.shu tdown(); }.
        >
        .shutdown() might throw, and I don't feel like wrapping it in
        try{}catch(). ~Wrapper should not throw, hence I need a better system to
        break things down.
        >
        So which of us is right?
        (Scott Meyer's Effective C++ 3rd edition, Item 8 addresses exactly this.)


        How about something like (almost verbatim from the book):

        class BigSimCity
        {
        void shutdown()
        {
        // do stuff that may throw
        closed_ = true;
        }
        ~BigSimCity() // would this have an empty throw() specification?
        {
        if (!closed)
        {
        try { shutdown(); }
        catch (...) { // log or something }
        }
        }
        private:
        bool closed;
        };

        You give the client the chance to shutdown() on their own if they want
        catch it on their own. And if not, then shutdown()'s called by the
        destructor.

        Joe

        Comment

        • Cy Edmunds

          #5
          Re: Throw from a destructor


          "Phlip" <phlip2005@gEEE mail.comwrote in message
          news:pan.2006.0 7.07.20.30.46.6 14728@gEEEmail. com...
          C++ers:
          >
          I have this friend who thinks C++ cannot throw from a destructor.
          >
          I think throwing from a destructor is bad Karma, and should be designed
          around, but that C++ cannot strictly prevent the actual event.
          >
          (One compiler-oriented reason is a compiler might not be able to detect
          that the call-tree from a destructor leads to a throw.)
          >
          Put another way, suppose I have a big object, aBigSimCity, and it has a
          .shutdown() method. It must call before ~BigSimCity() calls, and it might
          throw. This implies my Wrapper class, containing aBigSimCity, should never
          declare a destructor like this: ~Wrapper() { aBigSimCity.shu tdown(); }.
          >
          .shutdown() might throw, and I don't feel like wrapping it in
          try{}catch(). ~Wrapper should not throw, hence I need a better system to
          break things down.
          >
          So which of us is right?
          >
          --
          Phlip
          There is nothing in the language to prevent a destructor from throwing. How
          could the compiler enforce such a rule? Some of the other replies mention
          using throw specifications but they don't help at all. Basically you should
          never allow your destructor to throw. If ~BigSimCity() can throw the guy who
          wrote it screwed up, not the client of the class.

          Cy


          Comment

          • Roland Pibinger

            #6
            Re: Throw from a destructor

            On Fri, 7 Jul 2006 22:14:31 GMT, Joe Van Dyk <joe.vandyk@boe ing.com>
            wrote:
            >Phlip wrote:
            >C++ers:
            >I have this friend who thinks C++ cannot throw from a destructor.
            >I think throwing from a destructor is bad Karma, and should be designed
            >around, but that C++ cannot strictly prevent the actual event.
            >
            >(Scott Meyer's Effective C++ 3rd edition, Item 8 addresses exactly this.)
            >How about something like (almost verbatim from the book):
            >
            >class BigSimCity
            >{
            void shutdown()
            {
            // do stuff that may throw
            closed_ = true;
            }
            ~BigSimCity() // would this have an empty throw() specification?
            {
            if (!closed)
            {
            try { shutdown(); }
            catch (...) { // log or something }
            }
            }
            private:
            bool closed;
            >};
            >
            >You give the client the chance to shutdown() on their own if they want
            >catch it on their own. And if not, then shutdown()'s called by the
            >destructor.
            The above works in some circumstances but is not a general solution.
            It forces the client to write try-catch blocks, e.g.

            try {
            // ...
            mySimCity.shutd own();
            } catch (SimCityExcepti on& e) {
            // do something
            } catch (...) {
            // do somethig else
            }

            In general, when the shutdown function produces meaningful output
            (exception or return value) for the user it cannot be called in the
            destructor.
            Take as an example (good and bad) iostreams. When the file is opened
            read-only then you can ignore the return value from close(). But when
            the file is opened for write you cannot ignore the return value from
            close(). Iostreams do not distinguish that cases and use the wrong
            default.
            Your example may be re-written in the following way:

            class BigSimCity
            {
            bool commit()
            {
            // do stuff that may throw
            closed_ = true;
            return closed_;
            }

            void rollback() throw()
            {
            // do stuff that cannot throw or for which you
            // can ignore exceptions and return values
            }

            ~BigSimCity() throw()
            {
            if (!closed)
            {
            rollback();
            }
            }
            private:
            bool closed;
            };

            Best wishes,
            Roland Pibinger

            Comment

            • Jim Langston

              #7
              Re: Throw from a destructor

              "Phlip" <phlip2005@gEEE mail.comwrote in message
              news:pan.2006.0 7.07.20.30.46.6 14728@gEEEmail. com...
              C++ers:
              >
              I have this friend who thinks C++ cannot throw from a destructor.
              >
              I think throwing from a destructor is bad Karma, and should be designed
              around, but that C++ cannot strictly prevent the actual event.
              >
              (One compiler-oriented reason is a compiler might not be able to detect
              that the call-tree from a destructor leads to a throw.)
              >
              Put another way, suppose I have a big object, aBigSimCity, and it has a
              .shutdown() method. It must call before ~BigSimCity() calls, and it might
              throw. This implies my Wrapper class, containing aBigSimCity, should never
              declare a destructor like this: ~Wrapper() { aBigSimCity.shu tdown(); }.
              >
              .shutdown() might throw, and I don't feel like wrapping it in
              try{}catch(). ~Wrapper should not throw, hence I need a better system to
              break things down.
              >
              So which of us is right?
              It's extremely simple to test. Just throw from a destructor and see if it's
              allowed.

              The answer is, yes, you can throw from a destructor but never ever should.
              The problem being, as I understand it, if there are *2* errors thrown. The
              compiler/OS can't handle this and terminates.


              Comment

              • Phlip

                #8
                Re: Throw from a destructor

                Jim Langston wrote:
                It's extremely simple to test. Just throw from a destructor and see if
                it's allowed.
                Please don't mislead the newbies.

                Newbies: Jim is teasing. You should never rely on your compiler's current
                behavior to learn how C++ works. It may always compile more leniently than
                the Standard, and its undefined behavior may appear to work correctly.

                --
                Phlip

                Comment

                • James Bannon

                  #9
                  Re: Throw from a destructor

                  Phlip wrote:
                  Jim Langston wrote:
                  >
                  >It's extremely simple to test. Just throw from a destructor and see if
                  >it's allowed.
                  >
                  Please don't mislead the newbies.
                  >
                  Newbies: Jim is teasing. You should never rely on your compiler's current
                  behavior to learn how C++ works. It may always compile more leniently than
                  the Standard, and its undefined behavior may appear to work correctly.
                  >
                  Don't we know it! However, the Standard is usually as clear as mud and there are so
                  many cross-references it is very difficult to know whether the clause you're actually
                  reading is not modified by some other clause somewhere else (I've been bitten several
                  times by this - even if I have followed the cross-references and notes). Besides
                  this, some of the meanings of various concepts are not spelled out explicitly. For
                  instance I still can't find the specific definition of the term POD. I know what a
                  POD is (roughly) but I can't find a specific definition in the Standard other than a
                  note to say that the acronym POD stands for "Plain Old Data".

                  In answer to the OP, AFAIK the language doesn't _prevent_ throwing an exception from
                  a destructor but it should _never_ be done for at least two reasons that I know of:

                  1. Leaking an exception from a destructor during stack unwinding when processing an
                  exception leads to an immediate call to terminate - bang your dead! (to use Herb
                  Sutter's language).

                  2. If, for some reason, your lucky enough not to have terminate called, what do you
                  do with the exception when you've caught it? Re-throw if you can't recover? You're in
                  terminate land. In any case, how do you resuscitate or kill a partially dead object
                  when you have no way of knowing what state the object is in because that information
                  isn't conveyed to you by the exception class?

                  Sutter and others advise writing destructors as if they had an empty throw
                  specification. That's good enough for me! If you do specify it, say ~T() throw()
                  {...}, at least if an exception is leaked then terminate will be called immediately.
                  However, most people don't use throw specifications at all primarily because they're
                  expensive at runtime and sometimes they don't buy you very much in the way of safety
                  (Try analysing a piece of code and see how many exceptions it can possibly throw then
                  list these in the throw specifications. Can you do it consistently without any
                  errors? I know I can't.)

                  The meaning of undefined behaviour is a book in itself but broadly speaking it means
                  "anything is possible" from (seemingly) correct behaviour to your computer taking off
                  and launching itself into orbit or an invisible pink elephant suddenly appearing in
                  the room. Francis Glassborow relates a story in his recent book of how undefined
                  behaviour can bite and I once brought down a departmental server by missing out a 1
                  from the parameter list in a call to read() - took me 4 days to track down the
                  problem and I got a formal verbal warning for the mistake.

                  Anyway, that's my rant for the day!

                  Jim.

                  Comment

                  • Noah Roberts

                    #10
                    Re: Throw from a destructor


                    Jim Langston wrote:
                    It's extremely simple to test. Just throw from a destructor and see if it's
                    allowed.
                    >
                    The answer is, yes, you can throw from a destructor but never ever should.
                    The problem being, as I understand it, if there are *2* errors thrown. The
                    compiler/OS can't handle this and terminates.
                    There are numerous problems with destructors throwing, not the least of
                    which is:

                    class RandDestThrow
                    {
                    public:
                    ~RandDestThrow( ) { if (!(rand() % 2)) throw 666; }
                    };

                    RandDestThrow * arr = new RandDestThrow[666];


                    try
                    {
                    delete [] arr;
                    } catch (...)
                    {
                    // What do we do exactly?
                    }

                    It is impossible to write exception safe code if any destructor can
                    throw. Destructors must therefore ALWAYS have the no-throw guarantee.
                    If this means having catch(...) then so be it...never allow exceptions
                    to escape a destructor...ne ver.

                    Comment

                    • Marcus Kwok

                      #11
                      Re: Throw from a destructor

                      James Bannon <james.bannon@n tlworld.comwrot e:
                      2. If, for some reason, your lucky enough not to have terminate
                      called, what do you do with the exception when you've caught it?
                      Re-throw if you can't recover? You're in terminate land. In any case,
                      how do you resuscitate or kill a partially dead object when you have
                      no way of knowing what state the object is in because that information
                      isn't conveyed to you by the exception class?
                      >
                      Sutter and others advise writing destructors as if they had an empty
                      throw specification. That's good enough for me! If you do specify it,
                      say ~T() throw() {...}, at least if an exception is leaked then
                      terminate will be called immediately.
                      Actually, it will call unexpected(), which I believe by default will
                      call terminate(), but this behavior can be overridden with
                      std::set_unexpe cted().
                      However, most people don't use
                      throw specifications at all primarily because they're expensive at
                      runtime and sometimes they don't buy you very much in the way of
                      safety

                      seems to agree.

                      --
                      Marcus Kwok
                      Replace 'invalid' with 'net' to reply

                      Comment

                      • James Bannon

                        #12
                        Re: Throw from a destructor

                        Marcus Kwok wrote:
                        James Bannon <james.bannon@n tlworld.comwrot e:
                        >2. If, for some reason, your lucky enough not to have terminate
                        >called, what do you do with the exception when you've caught it?
                        >Re-throw if you can't recover? You're in terminate land. In any case,
                        >how do you resuscitate or kill a partially dead object when you have
                        >no way of knowing what state the object is in because that information
                        >isn't conveyed to you by the exception class?
                        >>
                        >Sutter and others advise writing destructors as if they had an empty
                        >throw specification. That's good enough for me! If you do specify it,
                        >say ~T() throw() {...}, at least if an exception is leaked then
                        >terminate will be called immediately.
                        >
                        Actually, it will call unexpected(), which I believe by default will
                        call terminate(), but this behavior can be overridden with
                        std::set_unexpe cted().
                        >
                        > However, most people don't use
                        >throw specifications at all primarily because they're expensive at
                        >runtime and sometimes they don't buy you very much in the way of
                        >safety
                        >

                        seems to agree.
                        >
                        You're right of course, it does call unexpected() which, if not redefined, will call
                        terminate. But what can you do within std::set_unexpe cted() if a destructor
                        unexpectedly throws? By the time it gets to the unexpected handler you've no way of
                        knowing which destructor caused the problem, so you might as well terminate anyway
                        although it might be a bit more graceful that a core dump.

                        Jim.

                        Comment

                        • red floyd

                          #13
                          Re: Throw from a destructor

                          James Bannon wrote:
                          For instance I still can't find the specific definition of the term POD. I
                          know what a POD is (roughly) but I can't find a specific definition in
                          the Standard other than a note to say that the acronym POD stands for
                          "Plain Old Data".
                          See 3.9/10 and 9/4

                          Comment

                          • James Bannon

                            #14
                            Re: Throw from a destructor

                            red floyd wrote:
                            James Bannon wrote:
                            >For instance I still can't find the specific definition of the term
                            >POD. I know what a POD is (roughly) but I can't find a specific
                            >definition in the Standard other than a note to say that the acronym
                            >POD stands for "Plain Old Data".
                            >
                            See 3.9/10 and 9/4
                            Thanks red,
                            I have it! 9/4 is interesting as an English example of a self-referential definition. :)

                            Jim.

                            Comment

                            • Jim Langston

                              #15
                              Re: Throw from a destructor

                              "James Bannon" <james.bannon@n tlworld.comwrot e in message
                              news:jiBtg.4349 2$Z61.29849@new sfe4-win.ntli.net...
                              red floyd wrote:
                              >James Bannon wrote:
                              >>For instance I still can't find the specific definition of the term
                              >>POD. I know what a POD is (roughly) but I can't find a specific
                              >>definition in the Standard other than a note to say that the acronym POD
                              >>stands for "Plain Old Data".
                              >>
                              >See 3.9/10 and 9/4
                              >
                              Thanks red,
                              I have it! 9/4 is interesting as an English example of a self-referential
                              definition. :)
                              Really? I'd like to read it but don't have the standard. Is it something
                              like the definition of recursion?

                              Recursion: See recursion.


                              Comment

                              Working...