David
David

Reputation: 28178

Is there any way to catch an exception thrown while constructing a static/global?

Consider the following horribleness:

#include <iostream>

struct thing
{
    thing()
    { 
        std::cout << "thing ctor\n";
        throw 5; 
    }
};

void SomeTerminateHandler()
{
    std::cout << "Uncaught exception!?\n";
}

int IHopeThisGetsCalledFirst()
{
    std::cout << "IHopeThisGetsCalledFirst()\n";
    std::set_terminate(SomeTerminateHandler);
    return 2;
}

const int x = IHopeThisGetsCalledFirst();
const thing y;

int main()
{
}

Output:

IHopeThisGetsCalledFirst()
thing ctor
Uncaught exception!?

This relies on static initialization order (which I can mess with, for MSVS anyway) so it's not ideal even if the following questions have acceptable answers.

Is there anything I can do to handle exceptions in static initialization time. If not, why not? Why isn't there something in the language to allow this?

Upvotes: 0

Views: 838

Answers (3)

stinky472
stinky472

Reputation: 6797

Is there anything I can do to handle exceptions in static initialization time. If not, why not? Why isn't there something in the language to allow this?

C++ doesn't have any well-defined constructs for user-defined objects externally linked or created at file scope. It's generally problematic with issues like undefined initialization order. Without a clear definition of what should happen with respect to things like initialization order, it would be difficult to define meaningful try/catch behavior (where would one put the try/catch block, e.g., if any object constructed in any compilation unit in a system can throw?).

For a more philosophical answer, I'd venture to guess that one of the problems C++ aims to address is the need for a lot of global objects. The language doesn't bother to provide well-defined behavior for them as they should generally be avoided in the first place.

If you want a quick fix, we can construct things in a lazy fashion:

thing& my_thing()
{
    static thing obj;
    return obj;
}

This ensures that thing won't be constructed until this function is first called which means it will not be constructed outside of your main entry point unless you have another global object which is calling this (directly or indirectly) through its constructor.

Ultimately it would be better if you avoid global instances of user-defined types this way all together, but if you can't resist the temptation, I recommend at least applying a form of lazy initialization liberally.

Upvotes: 1

Mike Seymour
Mike Seymour

Reputation: 254431

What can I do in SomeTerminateHandler to 'catch' the exception so I can display an error dialog box or log out the details of the error?

Nothing - only exception handlers can access the thrown exception.

abort is called after SomeTerminateHandler in the above program. Why?

Your terminate handler must not return; the standard (C++11, 18.8.3.1) requires that it "shall terminate execution of the program without returning to the caller" - the page you link to also says the same thing. If you break that requirement, then anything could happen.

Is there anything I can do to handle exceptions in static initialization time?

You can catch the exception in the constructor; or you can avoid complex static objects.

If not, why not? Why isn't there something in the language to allow this?

I can't answer why, but C++ doesn't handle complex static objects at all well. Even if your compiler does have non-standard extensions to specify the initialisation order, I suggest avoiding them if at all possible.

Upvotes: 2

BЈовић
BЈовић

Reputation: 64213

Is there anything I can do to handle exceptions in static initialization time. If not, why not?

No, because the initialization order is not specified by the standard.

The only thing is not to do it, or at least minimize number of objects that are initialized before main. It would be also very good to put them all in one place.

abort is called after SomeTerminateHandler in the above program. Why? This says "The terminate handler by default calls cstdlib's abort function." - but I'm not using the default terminate handler, and mine doesn't call abort.

Your handler is most likely not initialized on time (the exception is thrown before your handler is initialized).

Upvotes: 0

Related Questions