http://qs1969.pair.com?node_id=486368

rvosa has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I am trying to understand how to use Exception::Class in a large-ish multimodule application. As part of the set of modules I made one package where I am planning to define all exceptions, i.e.:
package Bio::Phylo::Exceptions; use Exception::Class ( 'Bio::Phylo::Exceptions', 'Bio::Phylo::Exceptions::BadNumber' => { isa => 'Bio::Phylo::Exceptions' }, 'Bio::Phylo::Exceptions::BadString' => { isa => 'Bio::Phylo::Exceptions' }, 'Bio::Phylo::Exceptions::BadFormat' => { isa => 'Bio::Phylo::Exceptions' }, 'Bio::Phylo::Exceptions::ObjectMismatch' => { isa => 'Bio::Phylo::Exceptions' } ); 1;
So then, elsewhere in the modules, there might be OO getters and setters like this:
use Scalar::Util qw(looks_like_number); sub set_number { my ( $self, $number ) = @_; looks_like_number $number ? $self->{'NUMBER'} = $number : Bio::Phylo::Exceptions::BadNumber->throw(error => 'bad number'); }
And then the user has to go through a contortion like:
# try eval { $obj->set_number(sdf7897) }; # catch if (UNIVERSAL::isa($@,'Bio::Phylo::Exceptions::BadNumber')){ # do something }
Is that how it works? Or are the exceptions caught inside the object methods?

Pardon my ignorance, but I've never worried about this before, and I wish to learn.

Thank you!

Replies are listed 'Best First'.
Re: Exception::Class - how to use?
by Arunbear (Prior) on Aug 24, 2005 at 22:14 UTC
    Yes, that is how it works (there's no point in catching the exception inside set_number because set_number is using the exception to signal failure to calling code).

    To make it a bit nicer, you could use Exception::Class::TryCatch in tandem with Exception::Class -

    use Exception::Class::TryCatch; ... # try eval { $obj->some_method('foo') }; # catch if ( catch my $err ) { $err->isa('Bio::Phylo::Exceptions::BadNumber') and do { handle_this( +$err) }; $err->isa('Bio::Phylo::Exceptions::ObjectMismatch') and do { handle_ +that($err) }; }

Re: Exception::Class - how to use?
by xdg (Monsignor) on Aug 24, 2005 at 23:24 UTC

    Arunbear's answer is a good one. I'd add that you want to be sure you know why you want to use Exception::Class. If you aren't planning to handle various exceptions, you almost might as well define some standard error messages and just die with them -- but then the major advantage of Exception::Class is that it's easier/saner/maintainable to determine what type of error occured from an object type than from parsing the error message.

    You want to consider the right place to use eval. Do you want to:

    • handle exceptions yourself
    • allow end-users to manage exceptions

    You should wrap routines with eval when you either know what the fallback behavior should be or you need to exit more gracefully than just allowing the program to die. (That's all the throw is -- just a die with an Exception::Class object instead of an error message.) And you want to wrap it at the right point for the error to be handled. That means probably not wrapping every little call, but rather wrapping as high up in the calling stack as possible:

    use Exception::Class::TryCatch; sub user_input_loop() { USER_INPUT: while ( my $line = <> ) { # try the command eval { process_command( $line ) }; # catch any error if( catch my $err ) { if( $err->isa('MyErr::Recoverable') ) { warn "Input error: $err\n"; next USER_INPUT; } else { # not recoverable warn "Unrecoverable error: $err\n"; $err->rethrow; } } } print "Exiting normally\n"; }

    You want to leave it to your end-users when the right thing to do in response to an error condition is up to them. Or you can stage it: catch the exception, do some cleanup, then rethrow the exception.

    It's not really much of a contortion if you consider what an alternative might be. If an end-user wants to know why some eval died, their only other option is to examine the $@ as a string. That's requires similar contortions and probably breaks the moment you change one of your error messages.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Thank you both for your clarifications - that's very helpful. I think I am just going to have to think hard in each situation whether I want to catch exceptions myself or leave it to the user. I suppose it depends, in some cases I might be able to do something gracefully, but in other cases the user is going to have to be told forcefully that their input isn't sane. I don't suppose there's a hard and fast rule for that, is there?
Re: Exception::Class - how to use?
by herveus (Prior) on Aug 25, 2005 at 10:47 UTC
    Howdy!

    You have the basic flow down, but the "catch" part can be simplified. Exception::Class provides a "caught" method that returns the exception object if it is of the right class or false. See "Catching Exceptions" in the Fine Manual for Exception::Class.

    yours,
    Michael
Re: Exception::Class - how to use?
by Perllace (Acolyte) on Apr 20, 2011 at 11:57 UTC
    Hi monks, I have a similar problem...I'm not sure how to use this package..moreover, I'm not sure if this package is actually right to meet my needs either.. Please have a look
    package MyExceptions; use strict; use warnings; use Exception::Class ( 'MyExceptions', 'MyExceptions::RegionNotFound' => { isa => 'MyExceptions' }, 'MyExceptions::CommandNotExecuted' => { isa => 'MyExceptions' } ); 1;

    Then I have another module to report the exceptions handled

    package ReportGenerator; use strict; use warnings; sub CheckResult { my ( $result, $info ) = @_; #Here is want to check of $result and throw an exception of the kin +d MyExceptions::RegionNotFound->throw(error => 'bad number'); #how do I do it?? }

    The user would script something like this

    $Report->CheckResult($ABC->Command(100,100),"Someinfo")
    Could you throw some light on this please..I have no idea is this kind of exception handling is the right way either..

      See Using Perl Exception Class.

      It makes little sense to ask the same question in two locations, especially in a thread that is 5 years old.

        Yes Corion..was in fix to get this done and... i didn't notice that the thread was 5 years old.. cud finally figure out how to do this ...
        use Exception::Class ( 'MyExceptions::Test' => { fields => [qw{message}], } ); use Moose::Util::TypeConstraints; class_type 'MyExceptions::ExecutionTest'; no Moose::Util::TypeConstraints; 1;
        then to throw the exception
        MyExceptions::ExceptionTest->throw( message => $data )
        then the script has something like
        if ( my $ex = $@ ) { my $e; if ( $e = Exception::Class->caught('MyExceptions::ExecutionTest')) { print $e->message; } }
        Thanks for your help