7

Is it possible to terminate software execution without skipping calls to destructors? For instance, in the code below, the destructor for test will never be called because of the exit(1) statement.

#include <iostream>
#include <cstdlib>
using namespace std;

class A{
public:
    A(){cout << "Constructed.\n";}
    ~A(){cout << "Destroyed.\n";}
};

void func()
{
    //Assuming something went wrong:
    exit(1);  
}

int main(int argc, char *argv[])
{
    A test;
    func();
    return 0;
}

What I need, is a way to end the program (from within func()) that calls all necessary destructors before terminating. So far I've been handling this through func() return value, as in:

bool func()
{
    //Assuming something went wrong:
    return false;
}

int main(int argc, char *argv[])
{
    A test;
    if( !func() )return 1;
    return 0;
}

The problem with this method is that it quickly becomes very annoying (and code bloating) to manage once you need to apply it to a series of nested functions.

Is there a way of achieving the same results of the second example (proper destructor calls) with a syntax similar to the first example (call exit(1) wherever you are)?

Malabarba
  • 4,473
  • 2
  • 30
  • 49
  • 4
    have you considered using exceptions? if you throw an exception in the function, destructors will get called. – Nerdtron Nov 29 '11 at 15:48
  • 1
    Don't use `exit`; rather, throw an exception and catch it at the `main` level. – Kerrek SB Nov 29 '11 at 15:50
  • 1
    This is the primary purpose of exceptions (to allow the execution of destructors). – Steven Lu Nov 29 '11 at 15:58
  • @Nerdtron: Slight correction: if you throw *and catch* an exception, destructors will get called. If it's not caught, then it's unspecified whether they are called. – Mike Seymour Nov 29 '11 at 16:05

4 Answers4

9

Throw an exception, catch it in main and return.

This relies on nothing else catching your exception without rethrowing it.

JoeG
  • 12,994
  • 1
  • 38
  • 63
  • The note about others catching your exception is an important one. If you want to be heavy-handed, you can throw some type other than an exception, something that would be unlikely to be handled by other code. Does anyone know if you can throw `nullptr`? – Michael Price Nov 29 '11 at 16:28
  • 1
    Oh, and of course, you cannot avoid the dreaded `catch(...)` syntax. – Michael Price Nov 29 '11 at 16:28
  • @Michael Price: You could make your own type and throw that. `catch(...)` could still be a problem. Hopefully any code doing that simply logs and re-throws the same exception. – Fred Larson Nov 29 '11 at 16:32
  • 2
    @Michael: best bet is just to define a class, call it `systemexit`, which takes the desired exit code as a constructor parameter. Throw that. If it doesn't have `std::exception` as a base class, then the only ways anyone can catch it are: (a) catch explicitly `systemexit`, in which case they know what it means so let's hope they're doing it for a good reason and will rethrow it; (b) `catch(...)`, in which case let's hope they're doing it for a good reason and will either rethrow or at worst terminate. Another risk is someone calls `func` from a `noexcept(true)` function: result is termination. – Steve Jessop Nov 29 '11 at 16:33
  • @SteveJessop - Even better since you can then overload the `()` operator on `systemexit` and in your catch block you can say `myexit();` (where `myexit` is the name of the object you caught). You can use this to call destructors for any globals that would not get called since they are not touch by the stack unwinding. – Michael Price Nov 29 '11 at 16:38
  • @Michael Price: But `main()` could return, which would destroy any globals. If there are global pointers (shudder), one would hope there's code at the end of `main()` to deal with that anyway. – Fred Larson Nov 29 '11 at 16:46
  • Do globals get cleaned up when you call `exit()`. I guess it makes sense that they should. – Michael Price Nov 29 '11 at 16:47
  • I actually tested this before asking and it didn't work. Turns out one of my pointers was causing unspecified behavior :-$. – Malabarba Nov 29 '11 at 16:50
5

You could rely on stack unwinding for this: when you want to exit, throw an exception and catch it in main().

Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012
1
struct my_exit
{
    int error;
    int operator()()
    {
        // do any cleanup on globals
        return error;
    }
};

int main()
{
    try
    {
        doSomethingThatCouldCauseExit();
    }
    catch (my_exit & me)
    {
        // Clean up globals now
        exit(me());
    }
}
Michael Price
  • 8,088
  • 1
  • 17
  • 24
  • As was pointed out in the comments on another answer, globals would get cleaned up simply by returning from main or by calling exit, so I'm not certain what my operator overloading would get you in the end. Perhaps I'll think of another usage for it. – Michael Price Nov 29 '11 at 16:58
0

There are several ways to do this cleanly.

One solution is to use the atexit function, which simply calls the given function pointer when the program terminates.

You would have to allocate all of your objects off the heap, maintain some global table with pointers to all instantiated class instances, and then simply iterate through the table delete ing each instance in the registered function.

Max DeLiso
  • 1,213
  • 11
  • 23