Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy exception specifications cannot be useful?
    text
    copied!<p>I have read lots of arguments about (not) using <code>throw(X)</code> in function signatures, and I think the way it's specified in ISO C++ (and implemented in current compilers) it's rather useless. But why compiler cannot simply <strong>enforce</strong> exception-correctness at compile time? </p> <p>If I write function/method definition that contains <code>throw(A,B,C)</code> in it's signature, the compiler should not have many problems in figuring out if implementation of given function is exception-correct. That means that function body has</p> <ul> <li>no <code>throw</code>'s other than <code>throw A; throw B; throw C;</code>;</li> <li>no function/method call that have throw-signatures less restrictive than <code>throw (A,B,C)</code>;</li> </ul> <p>, at least outside <code>try{}catch()</code> catching other thrown types. If compiler raises error if these requirements are not met, then all functions should be "safe" and no runtime functions like <code>unexpected()</code> would be required. All this would be assured at compile-time.</p> <pre><code>void fooA() throw (A){ } void fooAB() throw (A,B){ } void fooABC() throw (A,B,C){ } void bar() throw (A){ throw A(); // ok throw B(); // Compiler error fooA(); // ok fooAB(); // compiler error fooABC(); // compiler error try{ throw A(); // ok throw B(); // ok throw C(); // Compiler error fooA(); // ok fooAB(); // ok fooABC(); // compiler error } catch (B){} } </code></pre> <p>This would require that all non-C++ realm code was either <code>throw()</code> specified by default (<code>extern "C"</code> should assume it by default), or if there is some exception interoperability then appropriate headers (for C++ at least) should be <code>throw</code>-specified as well. Failure to do so could be compared to using headers with different function/method return type in different compilation units. While it's not producing warnings or errors, it's obviously wrong - and as thrown exceptions are part of signature, they should also match.</p> <p>If we enforce such constrains it would have three effects:</p> <ul> <li>It would remove all those implicit <code>try{}catch</code> blocks, otherwise required for runtime checking, thus improving exception-handling performance.</li> <li>The argument that "Exceptions are making our library too big, so we switch them off"; would disappear, as most of additional code lies in those unnecessary implicit throw/catch instructions at every function call. If code was properly <code>throw</code>-specified, most of it wouldn't be added by compiler.</li> <li>It would make most of the programing world furious, as nobody ever seemed to like exceptions. Now, as these are actually usable, we need to learn how to use them.</li> </ul> <p>If we used some compatibility compiler flags for older code it wouldn't break anything, but as new exception-code would be quicker, there would be a good motivation not to write new code using it.</p> <p>To summarize my question: Why such enforcement is not required by ISO C++? Are there any strong reasons for it not to be? I always thought that exceptions are just another function's return value, but one that is governed automatically, so you can avoid writing functions like</p> <pre><code>std::pair&lt;int, bool&gt; str2int(std::string s); int str2int(std::string s, bool* ok); </code></pre> <p>plus additional auto-destruction of variables and propagation through multiple functions up the stack so you don't need code like</p> <pre><code>int doThis(){ int err=0; [...] if ((err = doThat())){ return err; } [...] } </code></pre> <p>;and if you can require function to <code>return</code> only the correct type, why can't you require it to <code>throw</code> it?</p> <p>Why can't exception specifiers be better? Why weren't they made like I described from the start?</p> <p>PS I know there might be some issues with exceptions and templates - depending on answers to this question perhaps i will ask another one about it - for now let us just forget templates.</p> <p><strong>EDIT</strong> (in response to @NicolBolas):</p> <blockquote> <p>What kind of optimization could the compiler do with exception class X that it couldn't do with Y?</p> </blockquote> <p>Compare:</p> <pre><code>void fooA() throw (A){ } void fooAB() throw (A,B){ } void fooABC() throw (A,B,C){ } void bar() throw (){ try{ fooA(); // if (exception == A) goto A_catch fooAB(); // if (exception == A) goto A_catch // if (exception == B) goto B_catch fooABC(); // if (exception == A) goto A_catch // if (exception == B) goto B_catch // if (exception == C) goto C_catch } catch (A){ // :A_catch [...] } catch (B){ // :B_catch [...] } catch (C){ // :C_catch [...] } } </code></pre> <p>and:</p> <pre><code>void fooA(){ } void fooAB(){ } void fooABC(){ } void bar(){ try{ fooA(); // if (exception == A) goto A_catch; // if (exception == B) goto B_catch; // if (exception == C) goto C_catch; // if (exception == other) return exception; fooAB(); // if (exception == A) goto A_catch; // if (exception == B) goto B_catch; // if (exception == C) goto C_catch; // if (exception == other) return exception; fooABC(); // if (exception == A) goto A_catch; // if (exception == B) goto B_catch; // if (exception == C) goto C_catch; // if (exception == other) return exception; } catch (A){ // :A_catch [...] } catch (B){ // :B_catch [...] } catch (C){ // :C_catch [...] } } </code></pre> <p>Here I included some pseudo-code that compiler would have generated no assembly level. As you can see, knowing what exceptions you can get can reduce amount of code. If we had some additional variables to destroy here, additional code would have been even longer.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload