Help-Site Computer Manuals
Software
Hardware
Programming
Networking
  Algorithms & Data Structures   Programming Languages   Revision Control
  Protocols
  Cameras   Computers   Displays   Keyboards & Mice   Motherboards   Networking   Printers & Scanners   Storage
  Windows   Linux & Unix   Mac

Exception::Base
Lightweight exceptions

Exception::Base - Lightweight exceptions


NAME

Exception::Base - Lightweight exceptions


SYNOPSIS


  # Use module and create needed exceptions

  use Exception::Base (

    ':all',                            # import try/catch functions

    'Exception::Runtime',              # create new module

    'Exception::System',               # load existing module

    'Exception::IO',          => {

        isa => 'Exception::System' },  # create new based on existing

    'Exception::FileNotFound' => {

        message => 'File not found',

        isa => 'Exception::IO' },      # create new based on new

  );

  # try / catch

  try eval {

    do_something() or throw Exception::FileNotFound

                                message=>'Something wrong',

                                tag=>'something';

  };

  # Catch the Exception::Base and derived, rethrow immediately others

  if (catch my $e) {

    # $e is an exception object for sure, no need to check if is blessed

    if ($e->isa('Exception::IO')) { warn "IO problem"; }

    elsif ($e->isa('Exception::Eval')) { warn "eval died"; }

    elsif ($e->isa('Exception::Runtime')) { warn "some runtime was caught"; }

    elsif ($e->with(tag=>'something')) { warn "something happened"; }

    elsif ($e->with(qr/^Error/)) { warn "some error based on regex"; }

    else { $e->throw; } # rethrow the exception

  }

  # the exception can be thrown later

  $e = new Exception::Base;

  # (...)

  $e->throw;

  # try with array context

  @v = try [eval { do_something_returning_array(); }];

  # catch only IO errors, rethrow immediately others

  try eval { File::Stat::Moose->stat("/etc/passwd") };

  catch my $e, ['Exception::IO'];

  # immediately rethrow all caught exceptions and eval errors

  try eval { die "Bang!\n" };

  catch my $e, [];

  # don't use syntactic sugar

  use Exception::Base;          # does not import ':all' tag

  try Exception::Base eval {

    throw Exception::IO;

  };

  catch Exception::Base my $e;  # catch Exception::Base and derived

  # or

  catch Exception::IO my $e;    # catch IO errors and rethrow others


DESCRIPTION

This class implements a fully OO exception mechanism similar to the Exception::Class manpage or the Class::Throwable manpage. It does not depend on other modules like the Exception::Class manpage and it is more powerful than the Class::Throwable manpage. Also it does not use closures as Error and does not polute namespace as the Exception::Class::TryCatch manpage. It is also much faster than the Exception::Class manpage.

The features of the Exception::Base manpage:

  • fast implementation of an exception object

  • fully OO without closures and source code filtering

  • does not mess with $SIG{__DIE__} and $SIG{__WARN__}

  • no external modules dependencies, requires core Perl modules only

  • implements error stack, the try/catch blocks can be nested

  • shows full backtrace stack on die by default

  • the default behaviour of exception class can be changed globally or just for the thrown exception

  • the exception can be created with defined custom properties

  • matching the exception by class, message or custom properties

  • matching with string, regex or closure function

  • creating automatically the derived exception classes (``use'' interface)

  • easly expendable, see the Exception::System manpage class for example


IMPORTS

use Exception::Base qw< catch try >;
Exports the catch and try functions to the caller namespace.

  use Exception::Base qw< catch try >;

  try eval { throw Exception::Base; };

  if (catch my $e) { warn "$e"; }

use Exception::Base ':all';
Exports all available symbols to the caller namespace.

use Exception::Base 'Exception', ...;
Loads additional exception class module. If the module is not available, creates the exception class automatically at compile time. The newly created class will be based on the Exception::Base manpage class.

  use Exception::Base qw< Exception::Custom Exception::SomethingWrong >;

  throw Exception::Custom;

use Exception::Base 'Exception' => { isa => BaseException, version => version, ... };
Loads additional exception class module. If the module's version is lower than given parameter or the module can't be loaded, creates the exception class automatically at compile time. The newly created class will be based on given class and has the given $VERSION variable.
isa
The newly created class will be based on given class.

version
The class will be created only if the module's version is lower than given parameter and will have the version given in the argument.

message
verbosity
max_arg_len
max_arg_nums
max_eval_len
other field having default property
The class will have the default property for the given field.


  use Exception::Base

    'try', 'catch',

    'Exception::IO',

    'Exception::FileNotFound' => { isa => 'Exception::IO' },

    'Exception::My' => { version => 0.2 },

    'Exception::WithDefault' => { message => 'Default message' };

  try eval { throw Exception::FileNotFound; };

  if (catch my $e) {

    if ($e->isa('Exception::IO')) { warn "can be also FileNotFound"; }

    if ($e->isa('Exception::My')) { print $e->VERSION; }

  }
no Exception::Base qw< catch try >;
no Exception::Base ':all';
no Exception::Base;
Unexports the catch and try functions from the caller namespace.

  use Exception::Base ':all', 'Exception::FileNotFound';

  try eval { throw Exception::FileNotFound; };  # ok

  no Exception::Base;

  try eval { throw Exception::FileNotFound; };  # syntax error


CONSTANTS

FIELDS
Declaration of class fields as reference to hash.

The fields are listed as name => {properties}, where properties is a list of field properties:

is
Can be 'rw' for read-write fields or 'ro' for read-only fields. The field is read-only and does not have an accessor created if 'is' property is missed.

default
Optional property with the default value if the field value is not defined.

The read-write fields can be set with new constructor. Read-only fields are modified by the Exception::Base manpage class itself and arguments for new constructor will be stored in properties field.

The constant have to be defined in derivered class if it brings additional fields.


  package Exception::My;

  our $VERSION = 0.01;

  use base 'Exception::Base';

  # Define new class fields

  use constant FIELDS => {

    %{Exception::Base->FIELDS},       # base's fields have to be first

    readonly  => { is=>'ro', default=>'value' },  # new ro field

    readwrite => { is=>'rw' },                    # new rw field

  };

  package main;

  use Exception::Base ':all';

  try eval {

    throw Exception::My readonly=>1, readwrite=>2;

  };

  if (catch my $e) {

    print $e->{readwrite};                # = 2

    print $e->{properties}->{readonly};   # = 1

    print $e->{defaults}->{readwrite};    # = "value"

  }


FIELDS

Class fields are implemented as values of blessed hash. The fields are also available as accessors methods.

message (rw, default: 'Unknown exception')
Contains the message of the exception. It is the part of the string representing the exception object.

  eval { throw Exception message=>"Message", tag=>"TAG"; };

  print $@->{message} if $@;

properties (ro)
Contains the additional properies of the exception. They can be later used with ``with'' method.

  eval { throw Exception message=>"Message", tag=>"TAG"; };

  print $@->{properties}->{tag} if $@;

verbosity (rw, default: 3)
Contains the verbosity level of the exception object. It allows to change the string representing the exception object. There are following levels of verbosity:
  1. Empty string

  2. 
     Message
  3. 
     Message at %s line %d.

    The same as the standard output of die() function.

  4. 
     Class: Message at %s line %d
    
             %c_ = %s::%s() called at %s line %d
    
     ...

    The output contains full trace of error stack. This is the default option.

If the verbosity is undef, then the default verbosity for exception objects is used.

If the verbosity set with constructor (new or throw) is lower than 3, the full stack trace won't be collected.

If the verbosity is lower than 2, the full system data (time, pid, tid, uid, euid, gid, egid) won't be collected.

ignore_package (rw)
Contains the name (scalar) or names (as references array) of packages which are ignored in error stack trace. It is useful if some package throws an exception but this module shouldn't be listed in stack trace.

  package My::Package;

  use Exception::Base;

  sub my_function {

    do_something() or throw Exception::Base ignore_package=>__PACKAGE__;

  }

ignore_level (rw)
Contains the number of level on stack trace to ignore. It is useful if some package throws an exception but this module shouldn't be listed in stack trace. It can be used with or without ignore_package field.

  # Convert warning into exception. The signal handler ignores itself.

  use Exception::Base 'Exception::Warning';

  $SIG{__WARN__} = sub {

    Exception::Warning->throw(message => $_[0], ignore_level => 1)

  };

eval_error (ro)
Contains the original eval error message if the exception was rethrown from $@ variable. This message will be displayed as a part of the Exception::Base manpage message. The eval_error has line number, file name and line-feed (\n) removed from its message.

  try eval {

    eval { $a = $b = 0; $c = $a / $b };

    throw Exception::Base;

  };

  catch my $e;

  print $e->eval_error;

time (ro)
Contains the timestamp of the thrown exception. Collected if the verbosity on throwing exception was greater than 1.

  eval { throw Exception message=>"Message"; };

  print scalar localtime $@->{time};

pid (ro)
Contains the PID of the Perl process at time of thrown exception. Collected if the verbosity on throwing exception was greater than 1.

  eval { throw Exception message=>"Message"; };

  kill 10, $@->{pid};

tid (ro)
Constains the tid of the thread or undef if threads are not used. Collected if the verbosity on throwing exception was greater than 1.

uid (ro)
euid (ro)
gid (ro)
egid (ro)
Contains the real and effective uid and gid of the Perl process at time of thrown exception. Collected if the verbosity on throwing exception was greater than 1.

caller_stack (ro)
Contains the error stack as array of array with informations about caller functions. The first 8 elements of the array's row are the same as first 8 elements of the output of caller() function. Further elements are optional and are the arguments of called function. Collected if the verbosity on throwing exception was greater than 1. Contains only the first element of caller stack if the verbosity was lower than 3.

  eval { throw Exception message=>"Message"; };

  ($package, $filename, $line, $subroutine, $hasargs, $wantarray,

  $evaltext, $is_require, @args) = $@->{caller_stack}->[0];

max_arg_len (rw, default: 64)
Contains the maximal length of argument for functions in backtrace output. Zero means no limit for length.

  sub a { throw Exception max_arg_len=>5 }

  a("123456789");

max_arg_nums (rw, default: 8)
Contains the maximal number of arguments for functions in backtrace output. Zero means no limit for arguments.

  sub a { throw Exception max_arg_nums=>1 }

  a(1,2,3);

max_eval_len (rw, default: 0)
Contains the maximal length of eval strings in backtrace output. Zero means no limit for length.

  eval "throw Exception max_eval_len=>10";

  print "$@";

defaults (rw)
Meta-field contains the list of default values.

  my $e = new Exception;

  print defined $e->{verbosity}

    ? $e->{verbosity}

    : $e->{defaults}->{verbosity};


CONSTRUCTORS

new([%args])
Creates the exception object, which can be thrown later. The system data fields like time, pid, uid, gid, euid, egid are not filled.

If the key of the argument is read-write field, this field will be filled. Otherwise, the properties field will be used.


  $e = new Exception message=>"Houston, we have a problem",

                     tag => "BIG";

  print $e->{message};

  print $e->{properties}->{tag};

The constructor reads the list of class fields from FIELDS constant function and stores it in the internal cache for performance reason. The defaults values for the class are also stored in internal cache.

throw([%args]])
Creates the exception object and immediately throws it with die() function.

  open FILE, $file

    or throw Exception message=>"Can not open file: $file";


METHODS

throw([%args])
Immediately throws exception object. It can be used for rethrowing existing exception object. Additional arguments will override the fields in existing exception object.

  $e = new Exception::Base;

  # (...)

  $e->throw(message=>"thrown exception with overriden message");

  eval { throw Exception::Base message=>"Problem", fatal=>1 };

  $@->throw if $@->properties->{fatal};

throw($exception, [%args])
Immediately rethrows an existing exception object as an other exception class.

  eval { open $f, "w", "/etc/passwd" or throw Exception::System };

  # convert Exception::System into Exception::Base

  throw Exception::Base $@;

stringify([$verbosity[, $message]])
Returns the string representation of exception object. It is called automatically if the exception object is used in scalar context. The method can be used explicity and then the verbosity level can be used.

  eval { throw Exception; };

  $@->{verbosity} = 1;

  print "$@";

  print $@->stringify(3) if $VERY_VERBOSE;

It also replaces any message stored in object with the message argument if it exists. This feature can be used by derived class overwriting stringify method.

with(condition)
Checks if the exception object matches the given condition. If the first argument is single value, the message attribute will be matched. If the argument is a part of hash, the properties attribute will be matched or the attribute of the exception object if the properties attribute is not defined.

  $e->with("message");

  $e->with(tag=>"property");

  $e->with("message", tag=>"and the property");

  $e->with(tag1=>"property", tag2=>"another property");

  $e->with(uid=>0);

  $e->with(message=>'$e->{properties}->{message} or $e->{message}');

The argument (for message or properties) can be simple string or code reference or regexp.


  $e->with("message");

  $e->with(sub {/message/});

  $e->with(qr/message/);

try(eval)
The try method or function can be used with eval block as argument. Then the eval's error is pushed into error stack and can be used with catch later.

  try Exception::Base eval { throw Exception; };

  eval { die "another error messing with \$@ variable"; };

  catch Exception::Base my $e;

The try returns the value of the argument in scalar context. If the argument is array reference, the try returns the value of the argument in array context.


  $v = try Exception::Base eval { 2 + 2; }; # $v == 4

  @v = try Exception::Base [ eval { (1,2,3); }; ]; # @v = (1,2,3)

The try can be used as method or function.


  try Exception::Base eval { throw Exception::Base "method"; };

  Exception::Base::try eval { throw Exception::Base "function"; };

  Exception::Base->import('try');

  try eval { throw Exception::Base "exported function"; };

CLASS->catch([$variable])
The exception is popped from error stack written into the method argument. If the exception is not based on the CLASS, the exception is thrown immediately.

  try eval { throw Exception; };

  catch Exception::Base my $e;

  print $e->stringify(1);

If the error stack is empty, the catch method returns undefined value. It can be used in loop to clean up all unhandled exceptions.


  try eval { -f 'file1' or throw Exception::FileNotFound };

  try eval { -f 'file2' or throw Exception::FileNotFound };

  try eval { -f 'file3' or throw Exception::FileNotFound };

  while (catch my $e) {

      warn "$e" if not $e->isa('Exception::FileNotFound');

  }

If the $@ variable does not contain the exception object but string, new exception object is created with message from $@ variable with removed " at file line 123." string and the last end of line (LF).


  try eval { die "Died\n"; };

  catch Exception::Base my $e;

  print $e->stringify;

The method returns 1, if the exception object is caught, and returns 0 otherwise.


  try eval { throw Exception; };

  if (catch Exception::Base my $e) {

    warn "Exception caught: " . ref $e;

  }

If the method argument is missing, the method returns the exception object.


  try eval { throw Exception; };

  my $e = catch Exception::Base;

The catch can be used as method or function. If it is used as function, then the CLASS is Exception::Base by default.


  try eval { throw Exception::Base "method"; };

  Exception::Base->import('catch');

  catch my $e;  # the same as catch Exception::Base my $e;

  print $e->stringify;

CLASS->catch([$variable,] \@ExceptionClasses)
The exception is popped from error stack or returns undefined value if error stack is empty. If the exception is not based on the CLASS and is not based on one of the class from argument, the exception is thrown immediately.

  try eval { throw Exception::IO; }

  catch Exception::Base my $e, ['Exception::IO'];

  print "Only IO exception was caught: " . $e->stringify(1);

package
Returns the package name of the subroutine which thrown an exception.

file
Returns the file name of the subroutine which thrown an exception.

line
Returns the line number for file of the subroutine which thrown an exception.

subroutine
Returns the subroutine name which thrown an exception.


PRIVATE METHODS

_collect_system_data
Collect system data and fill the attributes of exception object. This method is called automatically if exception if thrown. It can be used by derived class.

  package Exception::Special;

  use base 'Exception::Base';

  use constant FIELDS => {

    %{Exception::Base->FIELDS},

    'special' => { is => 'ro' },

  };

  sub _collect_system_data {

    my $self = shift;

    $self->SUPER::_collect_system_data(@_);

    $self->{special} = get_special_value();

    return $self;

  }

  __PACKAGE__->_make_accessors;

  1;

Method returns the reference to the self object.

_make_accessors
Create accessors for each field. This static method should be called in each derived class which defines new fields.

  package Exception::My;

  # (...)

  __PACKAGE__->_make_accessors;


SEE ALSO

There are more implementation of exception objects available on CPAN:

Error
Complete implementation of try/catch/finally/otherwise mechanism. Uses nested closures with a lot of syntactic sugar. It is slightly faster than the Exception::Base manpage module. It doesn't provide a simple way to create user defined exceptions. It doesn't collect system data and stack trace on error.

the Exception::Class manpage
More perl-ish way to do OO exceptions. It is too heavy and too slow. It requires non-core perl modules to work. It missing try/catch mechanism.

the Exception::Class::TryCatch manpage
Additional try/catch mechanism for the Exception::Class manpage. It is also slow as the Exception::Class manpage.

the Class::Throwable manpage
Elegant OO exceptions without try/catch mechanism. It might be missing some features found in the Exception::Base manpage and the Exception::Class manpage.

Exceptions
Not recommended. Abadoned. Modifies %SIG handlers.

See also the Exception::System manpage class as an example for implementation of echanced exception class based on this the Exception::Base manpage class.


PERFORMANCE

The the Exception::Base manpage module was benchmarked with other implementations for simple try/catch scenario. The results (Perl 5.8.8 i486-linux-gnu-thread-multi) are following:

pure eval/die with string
381868/s

pure eval/die with object
137700/s

the Exception::Base manpage module with default options
5070/s

the Exception::Base manpage module with verbosity = 1
18979/s

Error module
17300/s

the Exception::Class manpage module
1540/s

the Exception::Class::TryCatch manpage module
1491/s

the Class::Throwable manpage module
7383/s

The the Exception::Base manpage module is 80 times slower than pure eval/die. This module was written to be as fast as it is possible. It does not use i.e. accessor functions which are slower about 6 times than standard variables. It is slower than pure die/eval because it is uses OO mechanism which are slow in Perl. It can be a litte faster if some features are disables, i.e. the stack trace and higher verbosity.

You can find the benchmark script in this package distribution.


TESTS

The module was tested with the Devel::Cover manpage and the Devel::Dprof manpage.


BUGS

If you find the bug, please report it.


AUTHOR

Piotr Roszatycki <dexter@debian.org>


LICENSE

Copyright (C) 2007 by Piotr Roszatycki <dexter@debian.org>.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See http://www.perl.com/perl/misc/Artistic.html

Programminig
Wy
Wy
yW
Wy
Programming
Wy
Wy
Wy
Wy