Exception handling (programming): Difference between revisions

Content deleted Content added
m Fix a syntax error
 
(12 intermediate revisions by 4 users not shown)
Line 169:
 
<syntaxhighlight lang="Java">
public void doIOOperationoperateOnFile(File f) throws IOException {
// ...
}
Line 195:
* Scalability: In a hierarchical design, each systems may have several subsystems. Each subsystem may throw several exceptions. Each parent system must deal with the exceptions of all subsystems below it, resulting in an exponential number of exceptions to be dealt with. Checked exceptions require all of these exceptions to be dealt with explicitly.
 
To work around these, Hejlsberg says programmers resort to circumventing the feature by using a {{C++|throws Exception}} declaration. Another circumvention is to use a {{C++|<syntaxhighlight lang="java" inline>try { ... } catch (Exception e) {<nowiki> ... } </nowikisyntaxhighlight> (or even a <syntaxhighlight lang="java" inline>try { ... } catch (Throwable t) { ... } </syntaxhighlight>) handler.<ref name=Trouble/> This is referred to as catch-all exception handling or '''Pokémon exception handling''' after [[Pokémon|the show]]'s catchphrase "[[Pokémon Theme|Gotta Catch ‘Em'Em All!]]".<ref>{{cite book |last1=Juneau |first1=Josh |title=Java 9 Recipes: A Problem-Solution Approach |date=31 May 2017 |publisher=Apress |isbn=978-1-4842-1976-8 |page=226 |url=https://books.google.com/books?id=TSYmDwAAQBAJ&pg=PA226 |language=en}}</ref> The Java Tutorials discourage catch-all exception handling as it may catch exceptions "for which the handler was not intended".<ref>{{cite web |url=http://download.oracle.com/javase/tutorial/essential/exceptions/advantages.html |title=Advantages of Exceptions (The Java™ Tutorials : Essential Classes : Exceptions) |publisher=Download.oracle.com |access-date=2011-12-15 |url-status=live |archive-url=https://web.archive.org/web/20111026121217/http://download.oracle.com/javase/tutorial/essential/exceptions/advantages.html |archive-date=2011-10-26 }}</ref> Still another discouraged circumvention is to make all exceptions subclass {{C++|RuntimeException}},<ref>{{cite web|url=http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html|title=Unchecked Exceptions – The Controversy (The Java™ Tutorials : Essential Classes : Exceptions)|publisher=Download.oracle.com|archive-url=https://web.archive.org/web/20111117042228/http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html|archive-date=2011-11-17|url-status=live|access-date=2011-12-15}}</ref>, thus making the exception unchecked. An encouraged solution is to use a catch-all handler or throws clause but with a specific [[superclass (computer science)|superclass]] of all potentially thrown exceptions rather than the general superclass {{C++|Exception}}. Another encouraged solution is to define and declare exception types that are suitable for the level of abstraction of the called method<ref>Bloch 2001:178 {{cite book | last = Bloch | first = Joshua | year = 2001 | title = Effective Java Programming Language Guide | publisher = Addison-Wesley Professional | isbn = 978-0-201-31005-4 | url-access = registration | url = https://archive.org/details/effectivejavapro00bloc }}</ref> and map lower level exceptions to these types by using [[exception chaining]].
 
=== Similar mechanisms ===
Line 201:
The roots of checked exceptions go back to the [[CLU programming language]]'s notion of exception specification.<ref name=Mindview/> A function could raise only exceptions listed in its type, but any leaking exceptions from called functions would automatically be turned into the sole runtime exception, {{code|failure}}, instead of resulting in compile-time error.<ref name="CLU">{{cite journal |last1=Liskov |first1=B.H. |last2=Snyder |first2=A. |title=Exception Handling in CLU |journal=IEEE Transactions on Software Engineering |date=November 1979 |volume=SE-5 |issue=6 |pages=546–558 |doi=10.1109/TSE.1979.230191 |s2cid=15506879 |url=http://csg.csail.mit.edu/CSGArchives/memos/Memo-155-3.pdf |access-date=19 December 2021}}</ref> Later, [[Modula-3]] had a similar feature.<ref>{{cite web |url=http://www1.cs.columbia.edu/graphics/modula3/tutorial/www/m3_23.html#SEC23 |title=Modula-3 - Procedure Types |publisher=.cs.columbia.edu |date=1995-03-08 |access-date=2011-12-15 |url-status=live |archive-url=https://web.archive.org/web/20080509143753/http://www1.cs.columbia.edu/graphics/modula3/tutorial/www/m3_23.html#SEC23 |archive-date=2008-05-09 }}</ref> These features don't include the compile time checking that is central in the concept of checked exceptions.<ref name=Mindview>{{cite web |url=http://www.mindview.net/Etc/Discussions/CheckedExceptions |archive-url=https://web.archive.org/web/20020405175011/http://www.mindview.net/Etc/Discussions/CheckedExceptions |url-status=dead |archive-date=2002-04-05 |title=Bruce Eckel's MindView, Inc: Does Java need Checked Exceptions? |publisher=Mindview.net |access-date=2011-12-15 }}</ref>
 
Early versions of the C++ programming language included an optional mechanism similar to checked exceptions, called '''exception specifications'''. By default any function could throw any exception, but this could be limited by a {{Cpp|throw}} clause (similar to the {{Java|throws}} clause in Java) added to the function signature, that specified which exceptions the function may throw.
 
<syntaxhighlight lang="C++">
Line 209:
</syntaxhighlight>
 
C++ <code>throw</code> clauses could specify any number of any types, even primitives and classes that did not extend <code>std::exception</code>.
Exception specifications were not enforced at compile-time. Violations resulted in the global function {{Cpp|std::unexpected}} being called.<ref name=bjarne-exc>[[Bjarne Stroustrup]], ''[[The C++ Programming Language]]'' Third Edition, [[Addison Wesley]], 1997. {{ISBN|0-201-88954-4}}. pp. 375-380.</ref> An empty exception specification could be given, which indicated that the function will throw no exception. This was not made the default when exception handling was added to the language because it would have required too much modification of existing code, would have impeded interaction with code written in other languages, and would have tempted programmers into writing too many handlers at the local level.<ref name=bjarne-exc/> Explicit use of empty exception specifications could, however, allow C++ compilers to perform significant code and stack layout optimizations that are precluded when exception handling may take place in a function.<ref name=cppeh /> Some analysts viewed the proper use of exception specifications in C++ as difficult to achieve.<ref>{{cite journal | title=Ten Guidelines for Exception Specifications | last=Reeves | first= J.W. | journal=C++ Report | volume=8 | issue=7 |date=July 1996}}</ref> This use of exception specifications was included in [[C++98]] and [[C++03]], [[deprecated]] in the 2012 C++ language standard ([[C++11]]),<ref>{{cite web |url=http://herbsutter.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/ |title=Trip Report: March 2010 ISO C++ Standards Meeting |last=Sutter |first=Herb |author-link=Herb Sutter |date=3 March 2010 |access-date=24 March 2010 |url-status=live |archive-url=https://web.archive.org/web/20100323082634/http://herbsutter.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/ |archive-date=23 March 2010 }}</ref> and was removed from the language in [[C++17]]. Throws clauses were replaced by {{Cpp|noexcept}} clauses. A function that will not throw any exceptions would now be denoted by the {{Cpp|noexcept}} keyword, and instead {{Cpp|noexcept(false)}} specified that a function will throw. One can also specify that a function is <code>noexcept</code> conditionally on another function being <code>noexcept</code>, like so:
 
Exception specifications were not enforced at compile-time. Violations resulted in the global function {{Cpp|std::unexpected}} being called.<ref name=bjarne-exc>[[Bjarne Stroustrup]], ''[[The C++ Programming Language]]'' Third Edition, [[Addison Wesley]], 1997. {{ISBN|0-201-88954-4}}. pp. 375-380.</ref> An empty exception specification could be given, which indicated that the function will throw no exception. This was not made the default when exception handling was added to the language because it would have required too much modification of existing code, would have impeded interaction with code written in other languages, and would have tempted programmers into writing too many handlers at the local level.<ref name=bjarne-exc/> Explicit use of empty exception specifications could, however, allow C++ compilers to perform significant code and stack layout optimizations that are precluded when exception handling may take place in a function.<ref name=cppeh /> Some analysts viewed the proper use of exception specifications in C++ as difficult to achieve.<ref>{{cite journal | title=Ten Guidelines for Exception Specifications | last=Reeves | first= J.W. | journal=C++ Report | volume=8 | issue=7 |date=July 1996}}</ref> This use of exception specifications was included in [[C++98]] and [[C++03]], [[deprecated]] in the 2012 C++ language standard ([[C++11]]),<ref>{{cite web |url=http://herbsutter.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/ |title=Trip Report: March 2010 ISO C++ Standards Meeting |last=Sutter |first=Herb |author-link=Herb Sutter |date=3 March 2010 |access-date=24 March 2010 |url-status=live |archive-url=https://web.archive.org/web/20100323082634/http://herbsutter.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/ |archive-date=23 March 2010 }}</ref> and was removed from the language in [[C++17]]. Throws clauses were replaced by {{Cpp|noexcept}} clauses. A function that will not throw any exceptions would now be denoted by the {{Cpp|noexcept}} keyword, and instead {{Cpp|noexcept(false)}} specified that a function will throw. OneAlthough can<code>throw</code> alsoclauses specifyare thatremoved afrom functionthe language, writing only <code>throw()</code> in the signature is legal and is equivalent to <code>noexcept</code> conditionally(no onexception anotherspecified functionby beingthe <code>noexceptthrow</code> clause denotes that it cannot throw). For transitioning a codebase that uses <code>throw</code> clauses, likethey can be adapted by instead redefining them with a macro to quickly resolve rejection of the removed <code>throw</code> clause by the so:compiler.
 
<syntaxhighlight lang="C++">
#define throw(...) noexcept(false)
</syntaxhighlight>
 
One can also specify that a function is <code>noexcept</code> conditionally on another function being <code>noexcept</code>, like so:
 
<syntaxhighlight lang="C++">
Line 215 ⟶ 223:
 
// The first noexcept is the noexcept clause, the second is the noexcept operator which evaluates to a Boolean value
void f() noexcept(noexcept(mightThrow()));
</syntaxhighlight>
 
Though C++ has no checked exceptions, one can propagate the thrown object up the stack when inside a <code>catch</code> block, by writing {{cpp|throw;}} (without specifying an object). This re-throws the caught object. This allows operations to be done within the <code>catch</code> block that catches it, before choosing to allow the object to continue propagating upwards.
 
An uncaught exceptions analyzer exists for the [[OCaml]] programming language.<ref>{{cite web |url=http://caml.inria.fr/pub/old_caml_site/ocamlexc/ocamlexc.htm |title=OcamlExc - An uncaught exceptions analyzer for Objective Caml |publisher=Caml.inria.fr |access-date=2011-12-15 |url-status=live |archive-url=http://archive.wikiwix.com/cache/20110806090555/http://caml.inria.fr/pub/old_caml_site/ocamlexc/ocamlexc.htm |archive-date=2011-08-06 }}</ref> The tool reports the set of raised exceptions as an extended type signature. But, unlike checked exceptions, the tool does not require any syntactic annotations and is external (i.e. it is possible to compile and run a program without having checked the exceptions).
 
In C++, one can also perform "Pokémon exception handling". Like <syntaxhighlight lang="java" inline>catch (Throwable t)</syntaxhighlight> in Java, C++ supports a <syntaxhighlight lang="cpp">catch (...)</syntaxhighlight> block, which will catch any thrown object. However, <code>catch (...)</code> has the disadvantage of not naming the caught object, which means it cannot be referred to.
 
<syntaxhighlight lang="cpp">
// Catching only exceptions:
try {
// ...
} catch (const std::exception& e) {
// Catching only exceptions:
std::println("An exception was caught: {}", e.what());
} catch (...) {
// Catching all thrown objects:
std::println("An unknown error was caught");
}
</syntaxhighlight>
 
The [[Rust (programming language)|Rust]] language, instead of using exceptions altogether, represents recoverable exceptions as [[result type]]s.<ref>{{cite web |title=std::result - Rust |url=https://doc.rust-lang.org/std/result/index.html |url-status=live |archive-url=https://web.archive.org/web/20231009032955/https://doc.rust-lang.org/std/result/index.html |archive-date=2023-10-09 |access-date=2023-10-09 |website=doc.rust-lang.org}}</ref><ref>{{cite web |date=2011-10-29 |title=stdlib: Add result module · rust-lang/rust@c1092fb |url=https://github.com/rust-lang/rust/commit/c1092fb6d88efe51e42df3aae2a321cc669e12a0 |url-status=live |archive-url=https://web.archive.org/web/20231009033047/https://github.com/rust-lang/rust/commit/c1092fb6d88efe51e42df3aae2a321cc669e12a0 |archive-date=2023-10-09 |access-date=2023-10-09 |website=github.com}}</ref> This is represented as <code>Result<T, E></code> (or <code>expected<T, E></code> in C++). The advantage of result types over checked exceptions is that while both result types and checked exceptions force users to immediately handle errors, they can also be directly represented as a return type within the language's type system, unlike checked exceptions where the declared potentially thrown exception is part of the function signature but not directly part of its return type.
 
== Dynamic checking of exceptions ==