Exceptions/Catch an exception thrown in a nested call

Revision as of 21:45, 28 January 2010 by Rdm (talk | contribs) (fix wiki eating placeholder argument)

Show how to create a user-defined exception and show how to catch an exception raised from several nested calls away.

  1. Create two user-defined exceptions, U0 and U1.
  2. Have function foo call function bar twice.
  3. Have function bar call function baz.
  4. Arrange for function baz to raise, or throw exception U0 on its first call, then exception U1 on its second.
  5. Function foo should catch only exception U0, not U1.
Task
Exceptions/Catch an exception thrown in a nested call
You are encouraged to solve this task according to the task description, using any language you may know.

Show/describe what happens when the program is run.

Ada

<lang ada>with Ada.Text_Io; use Ada.Text_Io;

procedure Exceptions_From_Nested_Calls is

  U0 : exception;
  U1 : exception;
  Baz_Count : Natural := 0;
  procedure Baz is
  begin
     Baz_Count := Baz_Count + 1;
     if Baz_Count = 1 then
        raise U0;
     else
        raise U1;
     end if;
  end Baz;
  procedure Bar is
  begin
     Baz;
  end Bar;
  procedure Foo is
  begin
     Bar;
  exception
     when U0 =>
        Put_Line("Procedure Foo caught exception U0");
  end Foo;

begin

  for I in 1..2 loop
     Foo;
  end loop;

end Exceptions_From_Nested_Calls;</lang> Sample output:

Procedure Foo caught exception U0

raised EXCEPTIONS_FROM_NESTED_CALLS.U1 : exceptions_from_nested_calls.adb:13

An unhandled exception leads to termination of the corresponding task. When the task is the main task of the program as in the example, the whole program is terminated. In the example the exception back tracing message is compiler-specific (in this case it is GNAT and further depends on the compiler options.

ALGOL 68

Translation of: python
Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386
Works with: ELLA ALGOL 68 version Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386

The following example follows the method used by ALGOL 68 for handling events in the language's transput prelude. Note that in the transput, then exception is effectively bound to the file handle, hence different file events can be caught by event handler associated to that particular file. Similarly the following example has bound two unique exceptions - u0 & u1 - to each unique instance of object.

c.f. ALGOL 68 Exceptions for more details. <lang algol68>MODE OBJ = STRUCT(

 INT value,
 STRUCT(
   STRING message,
   FLEX[0]STRING args,
   PROC(REF OBJ)BOOL u0, u1
 ) exception

);

PROC on u0 = (REF OBJ self, PROC (REF OBJ) BOOL mended)VOID:

 u0 OF exception OF self := mended;

PROC on u1 = (REF OBJ self, PROC (REF OBJ) BOOL mended)VOID:

 u1 OF exception OF self := mended;

PRIO INIT = 1, RAISE = 1;

OP INIT = (REF OBJ self, INT value)REF OBJ: (

 value OF self := value;
 u0 OF exception OF self := u1 OF exception OF self := (REF OBJ skip)BOOL: FALSE;
 args OF exception OF self := message OF exception OF self := "OBJ Exception";
 self

);

OP RAISE = (REF OBJ self, PROC (REF OBJ) BOOL mended)VOID:

 IF NOT mended(self) THEN
   put(stand error, (message OF exception OF self+" not caught - stop", new line));
   stop
 FI;

PROC (REF OBJ)VOID bar, baz; # early declaration is required by the ALGOL 68RS subset language #

PROC foo := VOID:(

   FOR value FROM 0 TO 1 DO
       REF OBJ i = LOC OBJ INIT value;
       on u0(i, (REF OBJ skip)BOOL: (GO TO except u0; SKIP ));
           bar(i);
       GO TO end on u0;
       except u0:
           print(("Function foo caught exception u0", new line));
       end on u0: SKIP
   OD

);

  1. PROC # bar := (REF OBJ i)VOID:(
   baz(i) # Nest those calls #

);

  1. PROC # baz := (REF OBJ i)VOID:
   IF value OF i = 0 THEN
       i RAISE u0 OF exception OF i
   ELSE
       i RAISE u1 OF exception OF i
   FI;

foo</lang> Output:

Function foo caught exception u0
OBJ Exception not caught - stop

Note: when an event occurs there are three possible responses.

  1. return false - in which case the default action takes place.
  2. mend the object and return true - date is mended and the program can continue from the point the event was raised.
  3. jump to an appropriately named label - effectively abandoning the offending section of code.

In the case of parallel processing, if the label is outside of the par clause, then all parallel the threads are terminated and the program continues in the parent thread.

AutoHotkey

AutoHotkey has very simple support for error tracking. The global ErrorLevel keeps track of the last error. Here is one way to keep track of nested errors: <lang AutoHotkey>foo() Return

foo() {

 bar(0)
 If InStr(ErrorLevel, "U0")
   MsgBox caught error: U0
 bar(1)
 If InStr(ErrorLevel, "U0")
   MsgBox caught error: U0

}

bar(i) {

 StringReplace, ErrorLevel, ErrorLevel, baz_error, , All   ; clear baz_error(s)
 If !baz(i)
   ErrorLevel .= "baz_error"  ; add baz_error to errorstack

}

baz(i) {

 StringReplace, ErrorLevel, ErrorLevel, U1, , All   ; clear U1 errors
 StringReplace, ErrorLevel, ErrorLevel, U0, , All   ; clear U0 errors
 If i
   ErrorLevel .= "U1"  ; add U1 errors to errorstack
 Else
   ErrorLevel .= "U0"
 Return 1

}</lang>

C++

First exception will be caught and message will be displayed, second will be caught by the default exception handler, which as required by the C++ Standard, will call terminate(), aborting the task, typically with an error message.

<lang cpp>#include <iostream> class U0 {}; class U1 {};

void baz(int i) {

   if (!i) throw U0();
   else throw U1();

} void bar(int i) { baz(i); }

void foo() {

   for (int i = 0; i < 2; i++)
   {   
       try {
           bar(i);
       } catch(U0 e) {

std::cout<< "Exception U0 caught\n";

       }
   }

}

int main() {

   foo();
   std::cout<< "Should never get here!\n";
   return 0;

}</lang>

Result:

Exception U0 caught
This application has requested the Runtime to terminate it in an unusual way.

The exact behavior for an uncaught exception is implementation-defined.

C#

This example will first catch U0 and print "U0 Caught" to the console when it does. The uncaught U1 exception will then cause the program to terminate and print the type of the exception, location of the error, and the stack.

<lang csharp>using System; //Used for Exception and Console classes class Exceptions {

 class U0 : Exception { }
 class U1 : Exception { }
 static int i;
 static void foo()
 {
   for (i = 0; i < 2; i++)
     try
     {
       bar();
     }
     catch (U0) {
       Console.WriteLine("U0 Caught");
     }
 }
 static void bar()
 {
   baz();
 }
 static void baz(){
   if (i == 0)
     throw new U0();
   throw new U1();
 }
 public static void Main()
 {
   foo();
 }

}</lang>

Output:

U0 Caught
Unhandled Exception: Exceptions+U1: Exception of type 'Exceptions+U1' was thrown.
   at Exceptions.baz() in Program.cs:line 27
   at Exceptions.bar() in Program.cs:line 22
   at Exceptions.foo() in Program.cs:line 14
   at Exceptions.Main() in Program.cs:line 32


Common Lisp

<lang lisp>(define-condition user-condition-1 (error) ()) (define-condition user-condition-2 (error) ())

(defun foo ()

 (dolist (type '(user-condition-1 user-condition-2))
   (handler-case
       (bar type)
     (user-condition-1 (c)
       (format t "~&foo: Caught: ~A~%" c)))))

(defun bar (type)

 (baz type))

(defun baz (type)

 (error type))    ; shortcut for (error (make-condition type))

(trace foo bar baz) (foo)</lang>

Sample output (the numbered lines are output from trace):

<lang lisp> 0: (FOO)

   1: (BAR USER-CONDITION-1)
     2: (BAZ USER-CONDITION-1)

foo: Caught: Condition USER-CONDITION-1 was signalled.

   1: (BAR USER-CONDITION-2)
     2: (BAZ USER-CONDITION-2)</lang>

At this point, the debugger (if any) is invoked with the unhandled condition of type USER-CONDITION-2.

D

First exception will be caught and message will be displayed, second will be caught by default exception handler.

<lang D>module test;

import tango.io.Stdout;

class U0 : Exception { this() { super("U0 error message"); } } class U1 : Exception { this() { super("U1 error message"); } }

void foo() {

   for (int i = 0; i < 2; i++)
   {   
       try {
           bar(i);
       } catch(U0 e) {
           Stdout ("Exception U0 caught").newline;
       }
   }

}

void bar(int i) { baz(i); } void baz(int i) {

   if (!i) throw new U0;
   else throw new U1;

}

void main() { foo(); }</lang>

Result:

Exception U0 caught
test.U1: U1 error message

If you have tango stack-trace, stack-trace will be print after second message.

Haskell

<lang haskell>import Control.Monad.Error import Control.Monad.Trans (lift)

-- Our "user-defined exception" tpe data MyError = U0 | U1 | Other deriving (Eq, Read, Show)

-- Required for any error type instance Error MyError where

 noMsg    = Other
 strMsg _ = Other

-- Throwing and catching exceptions implies that we are working in a monad. In -- this case, we use ErrorT to support our user-defined exceptions, wrapping -- IO to be able to report the happenings. ('lift' converts ErrorT e IO a -- actions into IO a actions.)

foo = do lift (putStrLn "foo")

        mapM_ (\toThrow -> bar toThrow                      -- the protected call
                             `catchError` \caught ->        -- the catch operation
                                                            -- ↓ what to do with it
                                case caught of U0 -> lift (putStrLn "foo caught U0")
                                               _  -> throwError caught)
              [U0, U1]                                      -- the two exceptions to throw
          

bar toThrow = do lift (putStrLn " bar")

                baz toThrow

baz toThrow = do lift (putStrLn " baz")

                throwError toThrow

-- We cannot use exceptions without at some outer level choosing what to do -- if an exception propagates all the way up. Here we just print the exception -- if there was one. main = do result <- runErrorT foo

         case result of
           Left e  -> putStrLn ("Caught error at top level: " ++ show e)
           Right v -> putStrLn ("Return value: " ++ show v)</lang>

The output of this program is:

foo
 bar
  baz
foo caught U0
 bar
  baz
Caught error at top level: U1

J

J leaves most of the implementation of exceptions to the programmer, so:

<lang J>foo=:3 :0

 for_i.0 1 do.
   try.bar i
   catcht.if.type_jthrow_-:'U0'do.smoutput'caught U0'else.throw.end.
   end.
 end.

)

bar=: baz

baz=:3 :0

 type_jthrow_=:'U',":y throw.

)</lang>

Example use:

   foo 9
caught U0
   type_jthrow_
U1

Java

Methods that may throw an exception (or that call a method that may throw an exception that it does not catch) must explicitly declare that they can throw such an exception (or a superclass thereof), unless they are unchecked exceptions (subclasses of RuntimeException or Error): <lang java>class U0 extends Exception { } class U1 extends Exception { }

public class ExceptionsTest {

   public static void foo() throws U1 {
       for (int i = 0; i <= 1; i++) {
           try {
               bar(i);
           } catch (U0 e) {
               System.out.println("Function foo caught exception U0");
           }
       }
   }
   public static void bar(int i) throws U0, U1 {
       baz(i); // Nest those calls
   }
   public static void baz(int i) throws U0, U1 {
       if (i == 0)
           throw new U0();
       else
           throw new U1();
   }
   public static void main(String[] args) throws U1 {
       foo();
   }

}</lang> Sample output:

Function foo caught exception U0
Exception in thread "main" U1
	at ExceptionsTest.baz(ExceptionsTest.java:23)
	at ExceptionsTest.bar(ExceptionsTest.java:16)
	at ExceptionsTest.foo(ExceptionsTest.java:8)
	at ExceptionsTest.main(ExceptionsTest.java:27)

The first line of the output is generated from catching the U0 exception in function foo.

Uncaught exceptions give information showing where the exception originated through the nested function calls together with the name of the uncaught exception, (U1) to stderr, then quit the running program.

JavaScript

Works with: Rhino
Works with: SpiderMonkey
Works with: Firefox

except for the print() function

The callee.name property, and the catch(e if ...) statement are Mozilla JavaScript extensions.

<lang javascript>function U() {} U.prototype.toString = function(){return this.className;}

function U0() {

   this.className = arguments.callee.name; 

} U0.prototype = new U();

function U1() {

   this.className = arguments.callee.name; 

} U1.prototype = new U();

function foo() {

   for (var i = 1; i <= 2; i++) {
       try {
           bar();
       }
       catch(e if e instanceof U0) {
           print("caught exception " + e);
       }
   }

}

function bar() {

   baz();

}

function baz() {

   // during the first call, redefine the function for subsequent calls
   baz = function() {throw(new U1());}
   throw(new U0());

}

foo();</lang> Rhino output:

caught exception U0
js: "nested_calls.js", line 31: exception from uncaught JavaScript throw: U1

SpiderMonkey output:

caught exception U0
uncaught exception: U1

OCaml

<lang ocaml>exception U0 exception U1

let baz i =

 raise (if i = 0 then U0 else U1)

let bar i = baz i (* Nest those calls *)

let foo () =

 for i = 0 to 1 do
   try
     bar i
   with U0 ->
     print_endline "Function foo caught exception U0"
 done

let () = foo ()</lang> Sample output:

Function foo caught exception U0
Exception: U1.

Oz

Any value can be raised as an exception. In this example, we simply use atoms.

Exceptions are caught by pattern matching. <lang oz>declare

 proc {Foo}
    for I in 1..2 do
       try
          {Bar I}
       catch u0 then {System.showInfo "Procedure Foo caught exception u0"}
       end
    end
 end
 proc {Bar I} {Baz I} end
 proc {Baz I}
    if I == 1 then
       raise u0 end
    else
       raise u1 end
    end
 end

in

 {Foo}</lang>

Sample output:

Procedure Foo caught exception u0

%****************************************************************
%**
%** Error: unhandled exception
%**
%** u1
%**--------------------------------------------------------------

Perl

<lang perl>sub foo {

   foreach (0..1) {
       eval { bar($_) };
       if ($@ =~ /U0/) { print "Function foo caught exception U0\n"; }
       else { die; } # propagate the exception
   }

}

sub bar {

   baz(@_); # Nest those calls

}

sub baz {

   my $i = shift;
   die ($i ? "U1" : "U0");

}

foo();</lang> Sample output:

Function foo caught exception U0
U1 at exceptionsnested.pl line 15.
	...propagated at exceptionsnested.pl line 5.

Python

There is no extra syntax to add to functions and/or methods such as bar, to say what exceptions they may raise or pass through them: <lang python>class U0(Exception): pass class U1(Exception): pass

def foo():

   for i in range(2):
       try:
           bar(i)
       except U0:
           print "Function foo caught exception U0"

def bar(i):

   baz(i) # Nest those calls

def baz(i):

   raise U1 if i else U0

foo()</lang> Sample output:

Function foo caught exception U0

Traceback (most recent call last):
  File "C:/Paddy3118/Exceptions_Through_Nested_Calls.py", line 17, in <module>
    foo()
  File "C:/Paddy3118/Exceptions_Through_Nested_Calls.py", line 7, in foo
    bar(i)
  File "C:/Paddy3118/Exceptions_Through_Nested_Calls.py", line 12, in bar
    baz(i) # Nest those calls
  File "C:/Paddy3118/Exceptions_Through_Nested_Calls.py", line 15, in baz
    raise U1 if i else U0
U1

The first line of the output is generated from catching the U0 exception in function foo.

Uncaught exceptions give information showing where the exception originated through the nested function calls together with the name of the uncaught exception, (U1) to stderr, then quit the running program.

R

The counter for the number of calls to baz is kept in the global environment for simplicity, but you could hide it in your own environment. See ?new.env and ?get. <lang r> number_of_calls_to_baz <- 0

foo <- function() {

  for(i in 1:2) tryCatch(bar())

}

bar <- function() baz()

baz <- function() {

  e <- simpleError(ifelse(number_of_calls_to_baz > 0, "U1", "U0"))
  assign("number_of_calls_to_baz", number_of_calls_to_baz + 1, envir=globalenv())   
  stop(e)

} </lang> Example Usage: <lang r> foo() # Error: U0 traceback() </lang>

6: stop(e) at file.r#11
5: baz()
4: bar()
3: tryCatchList(expr, classes, parentenv, handlers)
2: tryCatch(bar()) at file.r#4
1: foo()

Ruby

Uses a global variable to count the number of calls to baz <lang ruby>def foo

 begin
   bar
 rescue U0
   puts "captured exception U0"
 end

end

def bar

 baz

end

def baz

 raise $bazcount == 1 ? U0 : U1

end

class U0 < Exception end

class U1 < Exception end

for $bazcount in [1, 2]

 foo

end</lang>

$ ruby nested_calls.rb
captured exception U0
nested_calls.rb:14:in `baz': U1 (U1)
        from nested_calls.rb:10:in `bar'
        from nested_calls.rb:3:in `foo'
        from nested_calls.rb:24
        from nested_calls.rb:23:in `each'
        from nested_calls.rb:23

Wait, why does in `each' appear in the stack trace? There's no each in that code. Ruby translates this <lang ruby>for $bazcount in [1, 2]

 foo

end</lang> to this <lang ruby>[1, 2].each {|$bazcount| foo}</lang>

Tcl

Works with: Tcl version 8.5

<lang tcl>package require Tcl 8.5

proc foo {} {

   set code [catch {bar} ex options]
   if {$code == 1} {
       switch -exact -- $ex {
           U0      {puts "caught exception U0"} 
           default {return -options $options $ex ;# re-raise exception}
       }
   }

}

proc bar {} {baz}

  1. create an alias to pass the initial exception U0 to the baz proc

interp alias {} baz {} _baz U0

proc _baz {exception} {

   # re-set the alias so subsequent invocations will use exception U1
   interp alias {} baz {} _baz U1
   # throw
   return -code error $exception

}

foo foo</lang> Running this program results in:

$ tclsh85 exceptions.tcl
caught exception U0
U1
    while executing
"baz"
    (procedure "bar" line 1)
    invoked from within
"bar"
    (procedure "foo" line 2)
    invoked from within
"foo"
    (file "exceptions.tcl" line 26)

Ursala

Foo calls bar, and bar calls baz. Normal termination of bar is bypassed if baz raises an exception. The exception is caught or not by foo. <lang Ursala>#import std

baz =

~&?(

  ~&h?(
     :/'baz succeeded with this input:',
     <'baz threw a user-defined empty string exception','U1'>!%),
  <'baz threw a user-defined empty file exception','U0'>!%)

bar = :/'bar received this result from normal termination of baz:'+ baz

  1. executable&

foo =

guard(

  :/'foo received this result from normal termination of bar:'+ bar,
  'U0'?=z/~& :/'foo caught an exception with this error message:')</lang>

Note that the definition of bar includes no conditional (?) or exception handling operators, and is written without regard for any exceptions. Here is an example bash session.

$ echo "valid input" | foo
foo received this result from normal termination of bar:
bar received this result from normal termination of baz:
baz succeeded with this input:
valid input
$ foo < /dev/null
baz threw a user-defined empty file exception
U0
$ echo "" | foo
foo caught an exception with this error message:
baz threw a user-defined empty string exception
U1