tags:

views:

181

answers:

4

In certain cases - especially when an exception escapes a destructor during stack unwinding - C++ runtime calls terminate() which must do something reasonable post-mortem and then exit the program. When a question "why so harsh" arises the answer is usually "there's nothing more reasonable to do in such error situations". That sounds reasonable if the whole program is in C++.

Now what if the C++ code is in a library and the program that uses the library is not in C++? This happens quite often - for example I might have a native C++ COM component consumed by a .NET program. Once terminate() is called inside the component code the .NET program suddenly ends abnormally. The program author will first of all think "I don't care of C++, why the hell is this library make my program exit?"

How do I handle the latter scenario when developing libraries in C++? Is it reasonable that terminate() unexpectedly ends the program? Is there a better way to handle such situations?

+1  A: 

Suppose you have a function in C++ called cppfunc and you are invoking it from another langugage (such as C or .NET). I suggest you create a wrapper function, let's say exportedfunc, like so:

int exportedfunc(resultype* outresult, paramtype1 param1, /* ... */)
{
    try {
       *outresult = cppfunc(param1,param2,/* ... */);
       return 0; // indicate success
    }catch( ... ) {  // may want to have other handlers
       /* possibly set other error status info */
       return -1; // indicate failure
    }
}   

Basically, you need to ensure that exceptions do not cross language boundaries... so you need to wrap your C++ functions with a function that catches all exceptions and reports a status code or does something acceptable other than invoking std::terminate.

Michael Aaron Safyan
magic numbers? Why not just create message classes.
wheaties
This will not help against the cases when `terminate()` is called. Once during stack unwinding any new exception is thrown and escapes a destructor `terminate()` is called and no amount of try-catch earlier on the call stack can help against that.
sharptooth
@sharptooth, that is correct. However, if it is being called because an exception is crossing into a function that does not support exception handling, then this will fix that case.
Michael Aaron Safyan
@wheaties, you could use message classes if you wanted to do more than distinguish between success and failure. However, using a negative value (usually -1) for failure and 0 for success is standard convention in C.
Michael Aaron Safyan
@Michael Aaron Safyan: Yes, exceptions should never be propagated to non-C++ code. Any sane COM developer takes care of that.
sharptooth
A: 

The default terminate handler will call abort. If you don't want this behavior, define your own terminate handler and set it using set_terminate.

Vijay Mathew
And what will you do in your own handler? Standard requires terminating execution of the program without returning to the caller (18.6.3.1).
Tadeusz Kopec
Yeap, the key problem here is that returning to the caller is not allowed.
sharptooth
+5  A: 

I think the more fundamental issue isn't specifically what terminate does, but the library's design. The library may be designed to be used only with C++ in which case exceptions can be caught and handled as appropriate within the app.

If the library is intended to be used in conjunction with non-C++ apps, it needs to provide an interface that ensures no exceptions leave the library, for example an interface that does a catch(...).

Mark B
+10  A: 

Why is the C++ runtime calling terminate()? It doesn't do it at random, or due to circumstances which cannot be defined and/or avoided when the code is written. It does it because your code does something that is defined to result in a call to terminate(), such as throwing an exception from a destructor during stack unwinding.

There is a list in the C++ standard of all the situations which are defined to result in call to terminate(). If you don't want terminate() to be called, don't do any of those things in your code. The same applies to unexpected(), abort(), and so on.

I don't think this is any different really from the fact that you have to avoid undefined behavior, or in general avoid writing code which is wrong. You also have to avoid behavior which is defined but undesirable.

Perhaps you have a particular example where it is difficult to avoid a call to terminate(), but throwing an exception from a destructor during stack unwinding isn't it. Just don't throw exceptions out of destructors, ever. This means designing your destructors such that if they do something which might fail, the destructor catches the exception and your code continues in a defined state.

There are some situations where your system will impolitely hack your process away at the knees because of something that your C++ code does (although not by calling terminate()). For example, if the system is overcommitting memory and the VMM can't honour the promises malloc/new have made, then your process might be killed. But that's a system feature, and probably applies equally to the other language that's calling your C++. I don't think there's anything you can (or need to) do about it, as long as your caller is aware that your library might allocate memory. In that circumstance it's not your code's fault the process died, it's the defined response of the operating system to low-memory conditions.

Steve Jessop