sajas
sajas

Reputation: 1599

How can I catch exceptions thrown from constructor of a global instance of a class?

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

Answers (3)

really
really

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

sajas
sajas

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

iammilind
iammilind

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

Related Questions