3

Consider the following program

#include <iostream>
#include<cstdlib>
using namespace std;

class E {
   public:
      const char* error;
      E(const char* arg) : error(arg) { }
};

void my_terminate() {
  cout << "Call to my_terminate" << endl;
}

struct A {
  A() { cout << "In constructor of A" << endl; }
  ~A(){
    cout << "In destructor of A" << endl;
    throw E("Exception thrown in ~A()");
  }
};

struct B {
  B() { cout << "In constructor of B" << endl; }
  ~B() { cout << "In destructor of B" << endl; }
};

int main() {

  set_terminate(my_terminate);

  try {
    cout << "In try block" << endl;
    A a;
    B b;
    throw E("Exception thrown in try block of main()"); // Line 36
  }
  catch (E& e) {
    cout << "Exception: " << e.error << endl;
  }
  catch (...) {
    cout << "Some exception caught in main()" << endl;
  }

  cout << "Resume execution of main()" << endl;

}

Output:

In try block
In constructor of A
In constructor of B
In destructor of B
In destructor of A
Call to my_terminate

Disallowed system call: SYS_kill

In line 36 an exception is thrown from the try block in main. Now why is this exception not caught by the handler?

Rather the 'stack unwinding' process continues.The destructor of A throws an exception too which is again not caught by any handler,instead a call to my_terminate is made, why?

Why is the handler not called in the two cases?

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345

5 Answers5

17

The C++ rule is that you must never throw an exception from a destructor that is being called during the "stack unwinding" process of another exception.

You throw an exception in A's destructor, which is something you are not supposed to do.

Reunanen
  • 7,921
  • 2
  • 35
  • 57
  • In line 36 an exception is thrown from the try block in main.Now why is this exception not caught by the handler? "The stack unwinding process should continue after this". Correct me if I am wrong. – Prasoon Saurav Sep 27 '09 at 06:22
  • 3
    From the FAQ that I linked: "During stack unwinding, all the local objects in all those stack frames are destructed. If one of those destructors throws an exception (say it throws a Bar object), the C++ runtime system is in a no-win situation: should it ignore the Bar and end up in the } catch (Foo e) { where it was originally headed? Should it ignore the Foo and look for a } catch (Bar e) { handler? There is no good answer — either choice loses information. So the C++ language guarantees that it will call terminate() at this point, and terminate() kills the process. Bang you're dead." – Reunanen Sep 27 '09 at 06:23
  • But shouldn't the exception handler execute before the stack unwinding process when an exception is thrown in line 36 – Prasoon Saurav Sep 27 '09 at 06:25
  • 2
    No. Objects declared in the try {} scope need to be destructed first, because they are not visible outside, not even in the catch block. And because A's destructor behaves badly, the handler is never reached. – Reunanen Sep 27 '09 at 06:28
  • Original issue is not that user is throwing exception from destructor, As terminate is called , so this is expected behavior Reason is : in call my_terminate() program should exit not continue – Satbir Sep 27 '09 at 07:36
  • I agree that it is probably best never to let an exception __Escape__ a destructor. But saying it is a rule is too strong (some people will argue that failing fast is a desirable effect (not me)) – Martin York Sep 27 '09 at 08:13
  • The problem with exceptions escaping destructor is that if you use a type that does that with virtually any standard class template, you get U.B. (and not just `terminate`). Ditto for most third-party C++ libraries out there. It's not a hard requirement in theory, but in effect it triggers so much U.B. it's easier to treat it as one. – Pavel Minaev Jan 01 '10 at 12:28
3

When you throw the original E temporary from the try block in main, the runtime implementation constructs an exception object of type E and searches for the first catch block that can handle the exception. In this case, this is the immediately following catch (E& e) block.

When the implementation finds the correct catch to handle the exception, it then destroys all automatic variables which must go out of scope by moving out of the scope where the throw occurred to the scope in which the catch resides.

In this case the objects a and b which are local to the catch block go out of scope, so must be destroyed (in the reverse order that they were created). However, destroying a causes another exception to be thrown. Because the implementation already has an uncaught exception and has already chosen a catch handler for that exception which it is trying to reach, there is no mechanism to handle this new exception. The rule in this case is that std::terminate, and in your case your terminate handler, is called immediately.

You should note that your my_terminate function is not a conforming terminate_handler, as a terminate_handler must not return and must terminate the program execution (i.e. must not throw either). Yours returns implicitly.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
1

set_terminate

function installs term_func as the function called by terminate. set_terminate is used with C++ exception handling and may be called at any point in your program before the exception is thrown. terminate calls abort by default. You can change this default by writing your own termination function and calling set_terminate with the name of your function as its argument. terminate calls the last function given as an argument to set_terminate. After performing any desired cleanup tasks,

term_func should exit the program.

If it does not exit (if it returns to its caller), abort is called.

my_terminate() should look like :

void my_terminate() 
{
  cout << "Call to my_terminate" << endl;
  *
  *
  *
  exit(-1);

}
Community
  • 1
  • 1
Satbir
  • 6,358
  • 6
  • 37
  • 52
0

In section 15.2 of my draft standard it states:

3 The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” [ Note: If a destructor called during stack unwinding exits with an exception, std::terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note ]

They have defined "stack unwinding" broadly enough that it seems to cover this case, even if it is all happening within one function. I guess it's somewhat clear that implementations expect destructors not to try and propagate exceptions outward.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • "Don't see any obvious stack unwinding that would be happening" ? ... What about the throw in main? – Reunanen Sep 27 '09 at 06:31
  • Ok, but the catch is in main too, so wouldn't the stack level stay the same? In any case, they have defined "stack unwinding" so broadly that it certainly *does* seem to cover this case. – DigitalRoss Sep 27 '09 at 06:34
0

Here is the problem. Your A's destructor throws, which is a bad thing. Re-throwing exceptions, or throwing a new exception in an exception handler is kosher because the stack-unwinding is well-behaved. In that case, only one exception is alive at the current stack frame. When a destructor throws an exception during the stack unwinding process however, two exceptions are alive the same stack frame, i.e they unwind the stack on the same level. In your case, that's two E objects. So which one does one choose to follow? You cannot follow both, so the standard says that terminate() will be called. You can make the exception system use your custom terminate routine by passing one with std::set_terminate() from the header <exception>.

You seem to think that your std::terminate() handler can resume your program by returning, but that's undefined behavior.
If you absolutely positively need to throw from a destructor and can't contain it with an exception handler inside the destructor itself, here is a workaround:

The function uncaught_exception() from the header <exception> returns true if an exception has been thrown, but not yet caught. If it returns true, that means that the process is in the middle of a stack unwinding, unwinding the stack and calling destructors until it finds a proper exception handler. Use the function inside destructors which throw, so that they only throw when a stack unwinding is not happening.

Here's an example on how to use uncaught_exception() (Though, it's an extremely bad idea):

#include <iostream>
#include <exception>
#include <stdexcept>
#include <sstream>
#include <cstdlib>

void termhandler()
{
    std::cout << "Inside terminate()" << std::endl;
    abort();
}

class Foo
{
public:
    Foo(int val) : i(val){ std::cout << "Created Foo object " << i << std::endl; }
    ~Foo()
    {
        if(std::uncaught_exception()){
            std::cout << "~Foo::Foo() object " << i << " : " << "Stack unwinding in progress. Can't throw!" << std::endl;
        } else {
            std::cout << "~Foo::Foo() object " << i << " : " << "Throwing test exception." << std::endl;
            std::ostringstream strm;
            strm << i;
            std::runtime_error e("Exception from ~Foo::Foo() object " + strm.str());
            throw e;
        }
    }
    int i;
};

int main()
{
    try {
        std::set_terminate(termhandler);    
        Foo A(1);
        Foo B(2);
    } catch(std::exception& e){
        std::cout << "Caught exception in main() : " << e.what() << std::endl;
    }
}

Which gives the following output:

Created Foo object 1
Created Foo object 2
~Foo::Foo() object 2 : Throwing test exception.
~Foo::Foo() object 1 : Stack unwinding in progress. Can't throw!
Caught exception in main() : Exception from ~Foo::Foo() object 2

  • Note that I said __workaround__. I don't endorse use of std::uncaught_exception() in any way. I mentioned it only to aid in the explanation. –  Sep 27 '09 at 12:11