16

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?

sajas
  • 1,599
  • 1
  • 17
  • 39
  • 3
    This is a great question, can't believe it's been open for 2 days without so much as a comment! – j_random_hacker Dec 06 '12 at 04:55
  • 2
    @j_random_hacker, once it falls off of the top of `newest`, it's (usually) as good as lost – Brian Cain Dec 06 '12 at 05:25
  • 1
    I guess it's better to use set_terminate rather than set_unexpected if you throw an object outside of main function. because terminate() is the function that called if you throw an exception outside of try/catch block, unless you throw an excption object inside of a function of a type that's not in this function exception specification list. – AlexDan Dec 12 '12 at 15:55

3 Answers3

9

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.

sajas
  • 1,599
  • 1
  • 17
  • 39
  • 4
    The first solution has the advantage that there is actually a chance of _handling_ the exception and doing something useful. Often, you will still only just print a message and kill the process, but sometimes it's possible to recover from an exception. OTOH, the second solution is wrong, it should be using `set_terminate` (`unexpected` does _not_ handle uncaught exceptions, it handles exceptions that are not in accordance with a function's exception specification!). But even so, once in the terminate function, what can you do other than kill the process? There's nowhere you could return to. – Damon Dec 12 '12 at 15:44
  • 1
    I agree with above comment, when you throw an exception out of try/catch block, usually terminate function is the one that get called, except if you throw an exception object inside a function of a type that's not in the function exception epecification list. – AlexDan Dec 12 '12 at 15:51
  • @Damon . Yes. The reply that i got for the question also pointed to the same fact. The only thing that can be performed are some clean up activities. Thanks for pointing out the set_terminate method. I have made changes in the code based on set_terminate. If there is any error please help me out – sajas Dec 13 '12 at 03:41
0

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().

Community
  • 1
  • 1
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • But then , "The only way to gain control in case of throw from an initializer of a non local static object is set_unexpected()".. What does this statement mean? – sajas Dec 06 '12 at 05:22
  • @sajas, I am not sure what does it mean. Even with trying setting the handler before the exception doesn't help. May be we need to dig more into it. Need to see in what context this statement was written. – iammilind Dec 06 '12 at 05:27
  • I saw it in the book, The C++ Programming Language third edition, by Bjarne Stroustrup. It's in the section 14.7 Uncaught Exceptions, 2nd last para. – sajas Dec 06 '12 at 05:47
0

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.

really
  • 93
  • 7
  • _"`exit` does not unwind the stack, but **at this moment there are no destructors to be called by unwinding**"_ That's not necessarily true. If you have more than 1 global object with a non-static destructor, then those destructors won't be called. This is _only_ true if you only have 1 static global object with a non-static destructor. – Human-Compiler Jan 04 '23 at 18:11
  • @Human-Compiler, `exit()` [does call destructors in other static duration objects](https://en.cppreference.com/w/cpp/utility/program/exit). Such destructors are [not called by stack unwinding](https://en.cppreference.com/w/cpp/language/throw#Stack_unwinding), they're called when you `exit()` in your `catch` block. – really Jan 04 '23 at 23:36
  • 1
    Oh, interesting -- I didn't realize that objects with static storage duration actually had their destructors called from calls to `std::exit`; I thought it was the same for all objects. My apologies then! TIL. – Human-Compiler Jan 04 '23 at 23:41