3
votes

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?

5
Include the source code here. Links to external source code can break over time. - jmucchiello
I could not format the code that I posted here. So I gave an external link. - Prasoon Saurav

5 Answers

3
votes

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.

1
votes

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);

}
0
votes

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.

0
votes

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