{"id":1134,"date":"2013-07-10T01:00:00","date_gmt":"2013-07-09T23:00:00","guid":{"rendered":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1134"},"modified":"2013-07-08T23:33:26","modified_gmt":"2013-07-08T22:33:26","slug":"dont-take-exception","status":"publish","type":"post","link":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1134","title":{"rendered":"Don&#8217;t Take Exception"},"content":{"rendered":"<p>Exceptions were an enormous improvement for error handling in software. Previously we would have code like this:<\/p>\n<pre><code>int a() {\n   if( !do_something() )\n     return error_code_for_a;\n   return success_for_a;\n}\n\nint b() {\n   if( a() == error_code_for_a )\n     return error_code_for_b;\n   return success_for_b;\n}\n\nint c() {\n   if( b() == error_code_for_b )\n     return error_code_for_c;\n   return success_for_c;\n}\n\nint main() {\n   if( c() == error_code_for_c )\n     printf(&quot;Error in c()\\n&quot;);\n}<\/code><\/pre>\n<p>There are a few problems:<\/p>\n<ul>\n<li>In a large application, the generator of an exceptional case has no knowledge of how to handle that exception.<\/li>\n<li>The error therefore has to propagate from where it is generated to where it is handled.<\/li>\n<li>Each function has to have a way of returning an error code as well as its real result; right through the chain of calls. What happens if the valid range for the non-error return value covers every possible value for that data type? There is then no way to steal one value to indicate an error.<\/li>\n<\/ul>\n<p>Exceptions solve these problems by giving a separate return path for exceptional values.<\/p>\n<pre><code>int a() {\n   if( !do_something() )\n     throw exception_in_a;\n   return success_for_a;\n}\n\nint b() {\n   a();\n   return success_for_b;\n}\n\nint c() {\n   b();\n   return success_for_c;\n}\n\nint main() {\n   try {\n     c();\n   } catch( exception_in_a &amp;e ) {\n     printf(&quot;Error in c()\\n&quot;);\n   }\n}<\/code><\/pre>\n<p>Note that <code>b()<\/code> and <code>c()<\/code> have no ability to handle the exception, so they can simply ignore it \u00e2\u20ac\u201c the exception, should it occur, propagates right through them transparently.<\/p>\n<p>At first sight then exceptions solve our problems. Not so. As they come, we rely on all the information about the exception being known by the generator. That is often not the case. The classic example is a failure during read.<\/p>\n<pre><code>void readSomething( int fd ) {\n  if( read(fd, globalBuffer, 10) &lt; 0 )\n    throw libc_error(errno);\n}\n\nvoid openThenRead( const string &amp;fn ) {\n  fd = open(fn);\n  read(fd);\n}\n\nvoid getConfigFile() {\n  openThenRead(&quot;configfile.txt&quot;);\n}\n\nint main() {\n  try {\n    getConfigFile();\n  } catch( libc_error &amp;e ) {\n    cerr &lt;&lt; &quot;Error reading file, errno=&quot; &lt;&lt; e.errno() &lt;&lt; endl;\n  }\n}<\/code><\/pre>\n<p>What use is that as an error message? We have been told what the error was, but we have no way of finding out what file generated the error. Imagine of course that there are many files that may be opened in the application.<\/p>\n<p>Java, by custom, although not by necessity, deals with this problem by wrapping one exception in another.<\/p>\n<pre><code>void readSomething( int fd ) {\n  if( read(fd, globalBuffer, 10) &lt; 0 )\n    throw libc_error(errno);\n}\n\nvoid openThenRead( const string &amp;fn ) {\n  fd = open(fn);\n  try {\n      read(fd);\n  } catch( libc_error &amp;e ) {\n      throw open_and_read_error(fn, e);\n  }\n}\n\nvoid getConfigFile() {\n  openThenRead(&quot;configfile.txt&quot;);\n}\n\nint main() {\n  try {\n    getConfigFile();\n  } catch( open_and_read_error &amp;e ) {\n    cerr &lt;&lt; &quot;Error reading file, &quot; &lt;&lt; e.filename() &lt;&lt; endl;\n    try {\n      throw e.innerException();\n    } catch( libc_error &amp;e ) {\n      cerr &lt;&lt; &quot; - errno &quot; &lt;&lt; e.errno() &lt;&lt; endl;\n    }\n  }\n}<\/code><\/pre>\n<p>I\u00e2\u20ac\u2122ve laboured the point a little, but the idea should be clear; by wrapping one exception in another, all of the information about an exception can be gathered together.<\/p>\n<p>The problem is that this still has a major fault: the thrower ends up throwing at a particular target. If we have to write handlers for every combination of wrapper, and wrapped, to multiple depths, we will very quickly have to write far more handlers than is practical. Boost offers a <a href=\"http:\/\/www.boost.org\/doc\/libs\/1_54_0\/libs\/exception\/doc\/boost-exception.html\">pleasant mechanism<\/a> for tidying this up. I\u00e2\u20ac\u2122ll express the idea using a simplified syntax here, but you can get the real API from the Boost documentation.<\/p>\n<pre><code>void readSomething( int fd ) {\n  if( read(fd, globalBuffer, 10) &lt; 0 )\n    throw boost::exception() &lt;&lt; error_info_errno(errno);\n}\n\nvoid openThenRead( const string &amp;fn ) {\n  fd = open(fn);\n  try {\n    read(fd);\n  } catch( boost::exception &amp;e ) {\n    throw e &lt;&lt; error_info_filename(fn);  \n  }\n}\n\nvoid getConfigFile() {\n  openThenRead(&quot;configfile.txt&quot;);\n}\n\nint main() {\n  try {\n    getConfigFile();\n  } catch( boost::exception &amp;e ) {\n    if( e.has&lt;filename_t&gt;() )\n        cerr &lt;&lt; &quot;Error reading file, &quot; &lt;&lt; e.get&lt;filename_t&gt;() &lt;&lt; endl;\n    if( e.has&lt;errno_t&gt;() )\n        cerr &lt;&lt; &quot; - error number, &quot; &lt;&lt; e.get&lt;errno_t&gt;() &lt;&lt; endl;\n  }\n}<\/code><\/pre>\n<p>The point to observe is that <code>openThenRead()<\/code> catches the most generic type of error, and adds its own information to it as it passes. That same principle applies at all levels; meaning that no one ever has to catch more than one exception type.<\/p>\n<p>It\u00e2\u20ac\u2122s not my favoured option to use boost, as convenient as it is, as for me it\u00e2\u20ac\u2122s gotten too large and unstable (it gets updated a lot, and I\u00e2\u20ac\u2122ve had some builds fail on upgrades). In a future article I\u00e2\u20ac\u2122ll describe a simpler version of boost\u00e2\u20ac\u2122s exception class.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Exceptions were an enormous improvement for error handling in software. Previously we would have code like this: int a() { if( !do_something() ) return error_code_for_a; return success_for_a; } int b() { if( a() == error_code_for_a ) return error_code_for_b; return success_for_b; } int c() { if( b() == error_code_for_b ) return error_code_for_c; return success_for_c; } int\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.fussylogic.co.uk\/blog\/?p=1134\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[66,90,6],"_links":{"self":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1134"}],"collection":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1134"}],"version-history":[{"count":3,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1134\/revisions"}],"predecessor-version":[{"id":1140,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1134\/revisions\/1140"}],"wp:attachment":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}