Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "dynamic exception" guarantee cause overhead?

In C++11 this is deprecated:

void foo() throw();

and replaced by

void foo() noexcept;

In this article it is explained that the reason for this (among others, that boil down to the same thing) is that

C++ exception specifications are checked at runtime rather than at compile time, so they offer no programmer guarantees that all exceptions have been handled.

While this does make sense to me, I don't understand why throw() was checked dynamically in the first place, or why noexcept does not provide exception guarantee other than calling std::terminate instead of normal stack unwinding (which is not really a solid guarantee IMO).

Wouldn't it be possible to check whether exceptions are thrown or not during compile time and fail compilation if this happens? As I see it, there are basically three cases:

void foo() noexcept
{
    // 1. Trivial case
    throw myexcept();

    // 2. Try-catch case
    //    Necessary to check whether myexcept is derived
    //    from exception
    try 
    { 
        throw myexcept(); 
    } 
    catch(exception const & e)
    {}

    // 3. Nested function call
    //    Recursion necessary
    bar();
}

With templates in C++ being instantiated for every type, compiling applications takes forever anyways - so why not change noexcept to force the compiler to check whether exceptions are thrown during compile time?

The only difficulty I see is that a function may or may not throw depending on runtime states - but that function should not be allowed to call itself noexcept anyway in my opinion.

Am I missing something, or was the intent to not increase the compilation time further, or to go easy on the compiler developers?

like image 915
nijansen Avatar asked Nov 12 '22 00:11

nijansen


1 Answers

I think a lot of it came down to the fact that when exception specifications were being defined, compiler writers were well behind the power curve. Implementing C++98 as sufficiently complex that there's only ever been one compiler that even claimed to implement all its features. Every other compiler left out at least one major feature that was included in the standard. Most fairly openly admitted that they left out substantially more than that.

You also need to keep in mind that dynamic exception specifications were also considerably more complex than just throw(). It allows a programmer to specify an arbitrary set of types that can be thrown. Worse still, specifying that a function can throw foo means it can also throw anything derived from foo as well.

Enforcing exception specifications statically could have been done, but it would clearly have added quite a bit of extra work, and nobody was really sure what (if any) benefit it would provide. Under the circumstances, I think it was pretty easy for most to think that static enforcement was something that could be required later if there seemed to be enough use to justify the work. Changing from enforcing at run-time to compile-time wouldn't require modifying existing code, only existing implementations.

Another point is that I'm not sure there was ever really strong support of exception specifications anyway. I think there was general agreement on the basic idea, but when you get down to it, probably less about the details.

Bottom line: it was easy to mandate only dynamic enforcement, and leave static enforcement for later (if at all). Turns out, static enforcement probably wouldn't really add all that much positive in any case, so mandating it probably wouldn't have accomplished much anyway.

like image 54
Jerry Coffin Avatar answered Nov 15 '22 06:11

Jerry Coffin