Exception handling in class: question

M

mjl69

package MyClass;


use warnings;
use strict;

sub new
{
my $invocant = shift;
my $dbh = shift;
my $class = ref($invocant) || $invocant; # Object or class name
my $self = { };
$self->{_error} = '';
bless($self, $class);
return $self;
}
sub error
{
my $invocant = shift;
return $invocant->{_error};
}
### Is error handling ok or...
sub my_method
{
my $invocant = shift;
my $arg = shift;
if ($arg == 1)
{
eval
{
#do stuff
};
if ($@)
{
$invocant->{_error} = $@;
return undef;
}
else {return 'success'}
}
elsif ($arg ==2)
{
eval
{
#do stuff
};
if ($@)
{
$invocant->{_error} = $@;
return undef;
}
else {return 'success'}
}
elsif ($arg == 3)
{
eval
{
#do stuff
};
if ($@)
{
$invocant->{_error} = $@;
return undef;
}
else {return 'success'}
}
}
### Is this better?
sub my_method2
{
my $invocant = shift;
my $arg = shift;
eval
{
if ($arg == 1)
{
#do stuff
}
elsif ($arg ==2)
{
#do stuff
}
elsif ($arg == 3)
{
#do stuff
}
};
if ($@)
{
$invocant->{_error} = $@;
return undef;
}
else {return 'success'}
}
### Or, does it just depend on the situation?

### Thanks for advice - mjl
 
M

mjl69

[snip 23 lines of code]

### Is error handling ok or...

[snip 44 lines of code]

### Is this better?

[snip 26 lines of code]

### Or, does it just depend on the situation?

Come on.  What is the significant difference?  I'm not going to read
that much code to find out.


Anno

Should all methods be wrapped in a single eval block as standard practice for exception handling or should they be sprinkled through code where needed? I was just stating my question in Perl :)

mjl
 
A

Anno Siegel

mjl69 said:
[snip 23 lines of code]

### Is error handling ok or...

[snip 44 lines of code]

### Is this better?

[snip 26 lines of code]

### Or, does it just depend on the situation?

Come on.  What is the significant difference?  I'm not going to read
that much code to find out.


Anno

Should all methods be wrapped in a single eval block as standard
practice for exception handling or should they be sprinkled through code
where needed? I was just stating my question in Perl :)

No, you were hiding your question in Perl verbiage. Three or five lines
of uncommented code may be okay, near a hundred isn't. I didn't even see
you used "eval" in the code, not to mention the different ways you use it
in different parts.

As for your question, it is unclear why you use "eval" at all. It is not
the standard error handling mechanism in Perl. If you must use it, make
its scope as small as possible, that is, treat every case individually.
Only use it if the exception can't be otherwise anticipated. Don't catch
division by zero that way, for instance. Standard error handling is
through the standard module Carp.

OTOH, if you deliberately want to use "eval" for exception handling,
take a look at some of the "Exception" modules on CPAN, or write your
own. Don't use "eval" directly for that.

Anno
 
N

nobull

Anno said:
As for your question, it is unclear why you use "eval" at all. It is not
the standard error handling mechanism in Perl.

Er, it's one of them.
If you must use it, make
its scope as small as possible, that is, treat every case
individually.

I would agree with that.
Only use it if the exception can't be otherwise anticipated. Don't catch
division by zero that way, for instance.

I completely disagree with Anno on that. If you want to cope with the
possibility of numeric overflow you should figure out where it can
happen and wrap that code in eval{}. Trying to anticipate numeric
overflow is inefficient and error promne
Standard error handling is through the standard module Carp.

The use of Carp is orthogonal to the decision to use an a throw-catch
approach.
OTOH, if you deliberately want to use "eval" for exception handling,
take a look at some of the "Exception" modules on CPAN, or write your
own. Don't use "eval" directly for that.

Out of interest, why?
 
A

Anno Siegel

Er, it's one of them.

individually.

I would agree with that.


I completely disagree with Anno on that.

That seems to happen quite a bit, both ways. When it comes to pushing
the envelope, we seem to be pushing into opposite directions. There's
nothing wrong with that. We do seem to agree on most elementary matters.
If you want to cope with the
possibility of numeric overflow you should figure out where it can
happen and wrap that code in eval{}. Trying to anticipate numeric
overflow is inefficient and error promne

....especially with portability in view.

However, division by zero is the one case where numeric overflow *can*
be reliably predicted. If it's the only thing to worry about (as with
an integer denominator), I wouldn't recommend eval-catching the error.
Division of floats is another matter.
The use of Carp is orthogonal to the decision to use an a throw-catch
approach.

Carp does error attribution as its main feature, that is, it guesses an
entry on the call stack that is likely to be the place where the using
module introduced the error. With a throw-catch pair, that place is fully
under programmer control -- it is the location of the catcher. Using Carp
in addition could only mess things up. I don't see that as orthogonal.
Out of interest, why?

Well, that may have come out more dogmatic than it should have. If
exceptions are just that, i.e. rare, two or three cases may well be
handled with eval directly.

Before I'd sprinkle my code with lots of eval's (and associated "if
$@ ..."), I'd look for a smoother interface.

Anno
 
M

mjl69

No, you were hiding your question in Perl verbiage. Three or five lines
of uncommented code may be okay, near a hundred isn't. I didn't even see
you used "eval" in the code, not to mention the different ways you use it
in different parts.

As for your question, it is unclear why you use "eval" at all. It is not
the standard error handling mechanism in Perl. If you must use it, make
its scope as small as possible, that is, treat every case individually.
Only use it if the exception can't be otherwise anticipated. Don't catch
division by zero that way, for instance. Standard error handling is
through the standard module Carp.

"The eval keyword serves two distinct but related purposes in Perl.
These purposes are represented by two forms of syntax, evalBLOCK and evalEXPR.
The first form traps run-time exceptions (errors) that would otherwise prove fatal,
similar to the "try block" construct in C++ or Java.
The second form compiles and executes little bits of code on the fly at run time,
and also (conveniently) traps any exceptions just like the first form.
But the second form runs much slower than the first form, since it must parse the string every time.
On the other hand, it is also more general. Whichever form you use,
eval is the preferred way to do all exception handling in Perl."

-Programming Perl, 3rd ed. (2000)

Note, that I was talking about exception handling and not error handling. I may be wrong, but I had
the idea that my objects should not crash because of a fatal error, but continue and save the error
description to a string, array, or file and possibly take additional action based on the exception.
I realize that the above statement from "Programming Perl" might be considered outdated,
especially since the Exception class by Pete Jordan is dated 2004 and allows for greater control,
more similar to Java exception handling. The topic is more an issue of program structure rather than
language syntax so it may not be completely appropriate here.

mjl
 
A

Anno Siegel

[snip]
Note, that I was talking about exception handling and not error
handling.

What *is* the relation between errors and exceptions? That's a serious
question. I do have some ideas, but nothing I'd like to pontificate
about.

The question in your text was (embedded comments)

### Is error handling ok or...
### Is this better?

so you don't seem to be too sure of the distinction either.
I may be wrong, but I had
the idea that my objects should not crash because of a fatal error, but

Methods crash, objects don't.
continue and save the error
description to a string, array, or file and possibly take additional

Gee, a file? If you have an object, give it an error slot and put
the error message there.
action based on the exception.

That's an age-old question. A purist would say, no module (class or
otherwise) should die on the user unless specifically requested. The
decision should be up to the user. Recursively this means that the
decision always ends up in the main program.

Even if the module can't possibly do something useful in a situation,
the user may have an alternative approach, or just not care that much.
(Module authors can hardly imagine that. Don't I know it.)

Then again, that burdens users with having to check every time whether
they got what they asked for. The purist may be fundamentally right,
as purists are wont to be, but the user may not be grateful for the
freedom of choice.

Now, just because of "eval", the decision to die in a module isn't
as final as it seems. The user can re-gain control if needed. That
makes it possible to die in an error situation and still not give up
the purists demand, it's only harder. So in Perl, I'd say it's a
case-to-case decision.

If something is called in many places, is likely to be part of expressions,
is used in an overloaded operator, is high level, consider dying (croak)
in an error situation. If it is called in only one or two places, is
called in isolation, is low level, consider signalling an error (usually
by returning nothing: "return;") and providing a message.

To see how a potential user is going to call your code, use your crystal
ball. Sorry, no pat solution here.

Anno
 
M

mjl69

....
Now, just because of "eval", the decision to die in a module isn't
as final as it seems. The user can re-gain control if needed. That
makes it possible to die in an error situation and still not give up
the purists demand, it's only harder. So in Perl, I'd say it's a
case-to-case decision.

If something is called in many places, is likely to be part of expressions,
is used in an overloaded operator, is high level, consider dying (croak)
in an error situation. If it is called in only one or two places, is
called in isolation, is low level, consider signalling an error (usually
by returning nothing: "return;") and providing a message.

To see how a potential user is going to call your code, use your crystal
ball. Sorry, no pat solution here.

Anno

You're right, I am guilty of confusing the two. I did finally go with an array that is a class member to store specific error messages:

sub new
{
my $invocant = shift;
my $dbh = shift;
my $class = ref($invocant) || $invocant; # Object or class name
my $self = { };
$self->{_error} = [ ]; # to push errors into
bless($self, $class);
return $self;
}

and a sample of handling errors (not fully tested yet):

my @return = $dbh->selectrow_array( $query);
push @{$invocant->{_error}}, now().'DBI: '.$dbh->errstr if $dbh->err; #save DBI error to array
if ( not @return ) #save 'record not found' error to array
{
push @{$invocant->{_error}}, now().
'Record not found for: '.$querystring;
return undef;
}

I guess exceptions were not really my biggest issue after all. Sorry for the confusion and the poorly stated question.
I mostly needed a way to return undef for unsuccessful operations and still have access to detailed error information
for debugging. Thanks for your excellent detailed response.

mjl
 
A

Anno Siegel

[error/exception handling]
You're right, I am guilty of confusing the two. I did finally go with
an array that is a class member to store specific error messages:

What you call a class member it would call a field (or variable, or even
member) of the *object*. Oh well, OO terminology is funky.

[snip code]
and a sample of handling errors (not fully tested yet):

my @return = $dbh->selectrow_array( $query);
push @{$invocant->{_error}}, now().'DBI: '.$dbh->errstr if $dbh->err;
#save DBI error to array
if ( not @return ) #save 'record not found' error to array
{
push @{$invocant->{_error}}, now().
'Record not found for: '.$querystring;
return undef;
}

Just one point. It is usually better to signal an error by returning
nothing instead of returning undef. It's how *you* recognize the error,
the user should be able to do the same:

if ( my @result = $obj->meth( ...) ) {
# use @result
} else {
my $msg = $obj->error;
# deal with error
}

That doesn't work when you "return undef;". Use just "return;" or make
it explicit: "return ();"

Anno
 
T

Tassilo v. Parseval

Also sprach Anno Siegel:
Just one point. It is usually better to signal an error by returning
nothing instead of returning undef. It's how *you* recognize the error,
the user should be able to do the same:

if ( my @result = $obj->meth( ...) ) {
# use @result
} else {
my $msg = $obj->error;
# deal with error
}

When looking at this, I think that a BLOCK-eval wouldn't be that bad
afterall. What people like to forget is that die/croak's argument
doesn't have to be a string. It may also be an object which will then be
in $@. So if 'meth' is this:

package Class;
...
sub meth {
my $self = shift;
...
if (BAD_THING) {
croak Class::Exception->new(STRING);
}
}

package Class::Exception;
sub new { my ($class, $msg) = @_; bless \$msg => __PACKAGE__ }
sub msg { ${$_[0]} }

This could become:

my @result = eval { $obj->meth };
if ($@) {
die $@->msg;
}

The advantage besides mimicking the exception handling seen in other
languages is that '$obj' does not need an additional '_error' slot. It's
always nice not to have to change the structure of existing code.

Tassilo
 
M

mjl69

When looking at this, I think that a BLOCK-eval wouldn't be that bad
afterall. What people like to forget is that die/croak's argument
doesn't have to be a string. It may also be an object which will then be
in $@. So if 'meth' is this:

package Class;
...
sub meth {
my $self = shift;
...
if (BAD_THING) {
croak Class::Exception->new(STRING);
}
}

package Class::Exception;
sub new { my ($class, $msg) = @_; bless \$msg => __PACKAGE__ }
sub msg { ${$_[0]} }

This could become:

my @result = eval { $obj->meth };
if ($@) {
die $@->msg;
}

The advantage besides mimicking the exception handling seen in other
languages is that '$obj' does not need an additional '_error' slot. It's
always nice not to have to change the structure of existing code.

Tassilo

I think I have a feeling for the overall problem here, but please correct me if I'm wrong. It seems that exception handling
is a form of error handling and that's where the confusion comes up. A class could be set up to generate error messages
and put them somewhere, or, it could return undef or an error code (like 'system', which creates logic problems), or it could
generate an exception which will propagate up until it is handled or not handled. In Java, exception handling is an integral
part of the language. In many cases, you are forced to handle exceptions, even if your handling involves just passing them
along. In a language like Perl, where there are so many different ways to do things and things are done in so many different
ways, there is no real standard to error handling by all. For example, if I do this:

my @result = eval { $dbh->do($query) };
if ($@)
{
die "DBI error: $dbh->errstring();
}

the die function will never be called because DBI objects do not throw exceptions. Which leads back to my original question.
If I make my class fail-proof and it never fails, it may mess up someone else's error handling who wants to use my class.

mjl
 
T

Tassilo v. Parseval

Also sprach mjl69:
I think I have a feeling for the overall problem here, but please
correct me if I'm wrong. It seems that exception handling is a form of
error handling and that's where the confusion comes up. A class could
be set up to generate error messages and put them somewhere, or, it
could return undef or an error code (like 'system', which creates
logic problems), or it could generate an exception which will
propagate up until it is handled or not handled.

Right. And what exactly a method does when signalling errors is a matter
of documentation. Documentations are supposed to be read by those using
a library.
In Java, exception handling is an integral part of the language. In
many cases, you are forced to handle exceptions, even if your handling
involves just passing them along. In a language like Perl, where
there are so many different ways to do things and things are done in
so many different ways, there is no real standard to error handling by
all. For example, if I do this:

my @result = eval { $dbh->do($query) };
if ($@)
{
die "DBI error: $dbh->errstring();
}

the die function will never be called because DBI objects do not throw
exceptions. Which leads back to my original question. If I make my
class fail-proof and it never fails, it may mess up someone else's
error handling who wants to use my class.

Now, and that can only arise from not reading a class' documentation,
can't it? If I learn that a method/function might die under certain
circumstances, I certainly wrap it into a BLOCK-eval. If I learn that it
returns 'undef' on error, I check for undefinedness. Off the top of your
head, you can't know what means a method deploys to indicate failure. It
could be a false value, 'undef', it could die, it could even be -1. I
know of libraries whose constructors return an error string instead of a
blessed reference when something goes wrong.

The upshot of all that: There is no canonical way of error-reporting in
Perl. Asking for one wont let it spring into existence.

Tassilo
 
A

Anno Siegel

Tassilo v. Parseval said:
Also sprach Anno Siegel:
Just one point. It is usually better to signal an error by returning
nothing instead of returning undef. It's how *you* recognize the error,
the user should be able to do the same:

if ( my @result = $obj->meth( ...) ) {
# use @result
} else {
my $msg = $obj->error;
# deal with error
}

When looking at this, I think that a BLOCK-eval wouldn't be that bad
afterall. What people like to forget is that die/croak's argument
doesn't have to be a string. It may also be an object which will then be
in $@. So if 'meth' is this:

package Class;
...
sub meth {
my $self = shift;
...
if (BAD_THING) {
croak Class::Exception->new(STRING);
}
}

package Class::Exception;
sub new { my ($class, $msg) = @_; bless \$msg => __PACKAGE__ }
sub msg { ${$_[0]} }

This could become:

my @result = eval { $obj->meth };
if ($@) {
die $@->msg;
}

The advantage besides mimicking the exception handling seen in other
languages is that '$obj' does not need an additional '_error' slot. It's
always nice not to have to change the structure of existing code.

Hmmm... From this code I don't see the point in using an exception
object. Wouldn't a plain string do? I mean, if there were no
Class::Exception, and Class did

if (BAD_THING) {
croak STRING;
}

and the client did

my @result = eval { $obj->meth };
if ($@) {
die $@;
}

what would be the difference?

Anno
 
T

Tassilo v. Parseval

Also sprach Anno Siegel:
Hmmm... From this code I don't see the point in using an exception
object. Wouldn't a plain string do? I mean, if there were no
Class::Exception, and Class did

if (BAD_THING) {
croak STRING;
}

and the client did

my @result = eval { $obj->meth };
if ($@) {
die $@;
}

what would be the difference?

Arguably, there's not much of a difference if all the exception object
contains is a string. Those objects become more useful if they provide
information that would be difficult to wrap up in one string.

Tassilo
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,262
Messages
2,571,043
Members
48,769
Latest member
Clifft

Latest Threads

Top