Reputation: 1599
I know that having global variables is not appreciated. But in the book, The C++ programming language, by Bjarne Stroustrup, Author says that " The only way to gain control in case of throw from an initializer of a non local static object is set_unexpected() ". How is it done?
Upvotes: 16
Views: 3782
Reputation: 103
It's very easy to miss the point with error handling in C++, with exceptions, terminate
, abort
, and so on. The premise that regular code really fears to brake is that destructors will clean up everything constructed so far. So stack unwinding should be the focus, not the means you use for error handling, be it exceptions or something else.
To end the program in a catch
in main(), usually you just return an error, or exit(EXIT_FAILURE)
, so after the stack unwinding you already performed, static duration variables are also destroyed normally.
With global variables that are initialized before main()
, that's often all you need. You can preserve destructors functionality simply by using exit
instead of throw
in their constructors. Other global variables constructed so far are destroyed before the program ends, unlike with throw
in a default setup. exit
does not unwind the stack, but at this moment there are no destructors to be called by unwinding (except as edited below).
This might be a simpler solution for later readers with the problem in the question title.
EDIT: Besides automatic duration variables, stack unwinding also destroys completed bases and members of a class that throws on construction, which the exit
solution does not cover. If you have a global with subobjects that need destruction, you still have to wrap its constructor in a try/catch
, or manage its constructor failure manually. The catch
in this case needs to call exit
for other already constructed globals to be destroyed.
Upvotes: 0
Reputation: 1599
I posted the question in some other forums and got some answers.
First one was to declare a pointer rather than having an object and to initialize it in main()
Second one was to derive the class (whose constructor throws the exception ) from another class which performs set_terminate so as to set a meaningful handler. The second one seems to work fine in codeblocks ide.
The code I used to test check it is:
void f() //Unexpected exception handler
{
cout<<"Terminate called "<<endl;
exit (0);
}
class A //Base class that performs set_unexpected
{
terminate_handler h;
public:
A()
{
h=set_terminate(f);
}
~A()
{
set_terminate(h);
}
};
class B:public A //Derived class that throws unexpected_exception
{
public:
B()
{
throw 1;
}
};
B b;
int main()
{
}
The program displays the message: "Terminate called" and exits.
Upvotes: 9
Reputation: 69988
try/catch
block are within or along with function scope. Thus global objects cannot reside within try/catch
scope, so you can never catch those exceptions.
You can explore the special try/catch
syntax:
class Test {
public:
Test ()
try { throw 0; }
catch(...) {}
};
This provides some relief in handling such exceptions. Here is one thread discussing the same.
However, you have to provide such syntax for every class for which you declare global objects
You might be wondering that what's the difference if you put try/catch
inside the constructor itself? Well if a global object fails to initialize then the internal try/catch
will silently absorb it, which is bad.
But the above syntax will give you a chance for error diagnosis and then again rethrow the exception which will terminate the program.
See this thread for more details.
On the other hand, std::unexpected()
is called when there is no appropriate catch()
for a thrown exception. This function is standard, but you can configure your own by using std::set_unexpected()
.
Upvotes: 0