The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Exception - Exception handling and a base exception class.

SYNOPSIS

  use Exception qw(try catch throw);

  try { throw(new Exception('Error.')); };
  if(catch(qw(Exception e))) { 
    print 'Caught: ',$e->as_string(),"\n";
  }
  elsif(catch()) {
    print 'Caught Unknown: ',$@,"\n";
  }

DESCRIPTION

Exception handling routines. Execute try blocks, throw exceptions and catch them. This module also provides a base exception object.

API REFERENCE

new

  throw(new Exception('Error: ',$!));

Construct a new exception object. During construction, the object will capture the caller's state information, including a stacktrace by using the caller() function.

_exception__capture_exception_information

this is an internal function called by new() that actualy does the work of capturing the stacktrace and other state information. The information is then stored into the exception object.

get_stacktrace

  my @stack = Exception->get_stacktrace();

This method is called by _exception__capture_exception_information() during construction to capture the stacktrace. It returns an array of array references that represents the captured stacktrace. It can be invoked either as a method on an exception object, or as a package method (as the example above shows).

stacktrace_to_string

  print "Stacktrace:\n",Exception->stacktrace_to_string(@stack),"\n";
  # or
  print "Stacktrace:\n",$e->stacktrace_to_string(@stack),"\n";

This method takes an array of array references and turns them into a stringified summary that's nicer looking than the raw data structure.

line

  print 'At line: ',$e->line(),"\n";

Get or set the line number in the exception object.

package

  print 'In pacakge: ',$e->pacakge(),"\n";

Get or set the package name in the exception object.

filename

  print 'In File: ',$e->filename(),"\n";

Get or set the file name in the exception object.

stacktrace

  print 'Stacktrace: ',"\n",$e->stacktrace(),"\n";

This method returns (actualy, it's a get or set method) the stacktrace [string version creatd by stacktrace_to_string()]. By default, the stacktrace is generated and stored during the construction of the exception object.

error

  print 'Error: ',$e->error(),"\n";

Get or set the error in the exception object.

when

  print 'Hapened at: ',$e->when(),"\n";

Get or set the time in the exception object.

try

  try {
    ...
  };

Try a bock of code. This function is really just a synonym for eval, it's main reason for being here is to emulate the look of the code from other languages that support exception handling (like C++). Please note that try is actualy a function which expects to be passed a codeblock - this means that you need to follow your closing curly brace with a semicolon.

as_string

  print 'Exception: ',"\n",$e->as_string(),"\n";

Returns a nice looking summary of the inforamtion contained in the exception object.

catch

  if(catch(qw(My::Exception e))) {
    print 'Caught My::Exception object: ',$e->as_string(),"\n";
  }
  elsif(catch(qw(Exception e))) {
    print 'Caught Exception object: ',$e->as_string(),"\n";
  }
  elsif(catch()) {
    print 'Caught old style or unknown type of exception: ',$@,"\n";
  }

Try catching an exception. catch() expects its first argument to be a class name. If the exception that was thrown is of that type, or of a type derived from the given type, catch() will return a true value. If the exception is not an object of the given type, catch() will return undef.

If a second argument is passed to catch(), it will be assumed that it is to be a named scalar to store the exception object in. catch() will then create that symbol in the caller's namespace and assign the exception object to it. You don't have to use this, but if you do, $@ will be cleared (set to undef) so it doesn't accidentaly trigger any other code that might check $@. If you don't pass a symbol name here, you get to decide what to do with $@ (it will still contain the exception - weather its an object or not).

When you invoke catch() with no arguments, it will return true or false based on weather or not $@ is defined. This is how you can perform a 'catch all' for any exception type that you're not explicitly looking for.

throw

  unless(open(FILE,'</not/a/file')) {
    throw(new Exception('Errror opening file: ',$!,"\n"));
  }

Throw a new exception. throw() is technicaly just a synonym for Perl's die().

rethrow

  try {
    ...
  };
  if(catch(qw(Exception e))) {
    # handle the exception...
  }
  elsif(catch()) {
    # we don't know what this is...
    rethrow();
  }

rethrow() is mainly here for readability right now, though plans are to probably have the exceptoin object capture some kinds of information every time it's thrown or rethrown. rethrow() currently is exactly the same as throw() which is bacily just a synonym for die.

Called with no arguments, rethrow() throws $@. Called with an argument, rethrow() throws it's arguments.

DERIVING NEW EXCEPTION TYPES

Usualy the type of the exception is all that's importiant, so actualy deriving a new object from Exception and overriding behavior isn't commonly done. So, for Perl to recognize the new class type, all that really needs to be set up is the ISA array. So, all you will have to do is:

  @Test::Exception::ISA = qw(Exception);

Then use the new type:

  try {
    ...
  };
  if(catch(qw(Test::Exception e))) {
    # caught a Test::Exception...
  }
  elsif(catch(qw(Exception e))) {
    # caught an Exception...
  }
  elsif(catch()) {
    # caught something else...
  }

IMPLEMENTATION CONSIDERATIONS

I wanted to stay away from having to call eval() too much. It is possible to produce syntax that looks alot closer to C++ and Java, but it would have come at the cost of using eval more often. If for example, eval() was used for catch() blocks then we don't really have the ability to rethrow() a caught exception (since it's in an eval itself, it's throw gets captured in $@). This path started leading to a level of complexity that seemed unnecessary.

That is the same reason the decision to leave out finally() blocks. For a finally block to be properly implemented, eval would almost have been a necessity. Whe do you call the finally block? Right after the try? What if the finally block throws an exception? That situation would overwrite the try block's exception, which is probably undesierable. Waiting to call the finally block untill all of the catches were finished is also a difficult nut to crack.

These other syntaxes would also have make it harder to catch coding or syntax errors at compile time -- which is something that is usualy preferred to delaying them untill runtime.

AUTHOR

Kyle R. Burton <mortis@voicenet.com>

SEE ALSO

perl(1).