Subject: | Questions about TryCatch's philosophy |
Date: | Sun, 01 Mar 2009 19:36:44 +0100 |
To: | bug-Exception-Class-TryCatch [...] rt.cpan.org |
From: | Oliver Koch <QKoch [...] web.de> |
Message body not shown because it is not plain text.
use 5.8.8;
use strict;
use Data::Dumper 2.121;
use Exception::Class 1.26 (
'Ex::TestExceptionClass',
'Ex::TestExceptionClass::Test1' => { isa => 'Ex::TestExceptionClass' },
'Ex::TestExceptionClass::Test2' => { isa => 'Ex::TestExceptionClass' },
'Ex::TestExceptionClass::Test2Sub' => { isa => 'Ex::TestExceptionClass' },
'Ex::TestExceptionClass::Test3' => { isa => 'Ex::TestExceptionClass' },
'Ex::TestExceptionClass::Test3::Ex1' => { isa => 'Ex::TestExceptionClass::Test3' },
'Ex::TestExceptionClass::Test3::Ex2' => { isa => 'Ex::TestExceptionClass::Test3' },
'Ex::TestExceptionClass::Test3::Ex3' => { isa => 'Ex::TestExceptionClass::Test3' }
);
use Exception::Class::TryCatch 1.12;
# Declare prototypes.
sub Test1();
sub Test2Sub();
sub Test2();
sub Test3();
sub ExitProgram($);
print "$0 started.\n";
select STDERR; $| = 1; # make unbuffered
select STDOUT; $| = 1; # make unbuffered
# Initialize global variables.
my $TestNr = 0;
# ****************
$TestNr++;
print("$TestNr.) Simple try/throw/catch.\n");
print "\$@ = $@\n";
try eval {
throw Ex::TestExceptionClass
'Ex::TestExceptionClass thrown!';
};
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
print($Ex->message, "\n");
}
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
print("I would expect, that no exception was caught!\n");
print("Is exception still on stack?\n");
$@ = "";
} else {
print("No exception on stack.\n");
}
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
print("Is exception still on stack?\n");
} else {
print("No, but \$@ must be reset, too.\n");
}
# ****************
$TestNr++;
print("\n");
print("$TestNr.) Simple try/catch without throw.\n");
print "\$@ = $@\n";
try eval {
die("Just a simple die.");
};
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
print($Ex->message, "\n");
if($Ex->isa('Exception::Class::Base')) {
print("Why is a die() of class 'Exception::Class::Base'?\n");
print("I would expect something like 'Exception::Die'!\n");
} else {
print("Something unexpected happened (maybe a die).\n");
}
}
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
print("I would expect, that no exception was caught!\n");
print("Is exception still on stack?\n");
$@ = "";
} else {
print("No exception on stack.\n");
}
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
print("Is exception still on stack?\n");
} else {
print("No, but \$@ must be reset, too.\n");
}
# ****************
$TestNr++;
print("\n");
print("$TestNr.) throw in a subroutine caught outside.\n");
print "\$@ = $@\n";
Test1();
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
if($Ex->isa('Ex::TestExceptionClass::Test1')) {
print($Ex->message, "\n");
} else {
print("Unexpected exception!\n");
}
}
$@ = "";
# ****************
$TestNr++;
print("\n");
print("$TestNr.) throw and rethrow in subroutines caught outside, if not caught inside.\n");
print "\$@ = $@\n";
try eval {
Test2();
};
print "\$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
if($Ex->isa('Ex::TestExceptionClass::Test2')) {
print($Ex->message, "\n");
} else {
print("Unknown exception!\n");
print($Ex->message, "\n");
}
} else {
print("No further exceptions.\n");
}
$@ = "";
# ****************
$TestNr++;
print("\n");
print("$TestNr.) 3 throws and a die in a subroutine, stack cleaned up outside.\n");
print "\$@ = $@\n";
Test3();
print "\$@ = $@\n";
if(catch my $Ex) { # I miss a simple boolean function indicating,
try eval { $Ex->rethrow() }; # that's something on the exception stack!
$@ = ""; # Required to avoid endless loop.
while (catch my $Ex){
print("Caught ", ref($Ex), "\n");
if($Ex->isa('Ex::TestExceptionClass::Test3')) {
print($Ex->message, "\n");
} else {
print("Unexpected exception!\n");
print($Ex->message, "\n");
}
}
ExitProgram(-4);
}
ExitProgram(0);
sub Test1 () {
print "Test1: \$@ = $@\n";
try eval {
throw Ex::TestExceptionClass::Test1
'Ex::TestExceptionClass::Test1 thrown!';
};
print "Test1: \$@ = $@\n";
}
sub Test2Sub () {
print "Test2Sub: \$@ = $@\n";
#try eval {
throw Ex::TestExceptionClass::Test2Sub
'Ex::TestExceptionClass::Test2Sub thrown!';
#};
print "Test2Sub: \$@ = $@\n";
}
sub Test2 () {
print "Test2: \$@ = $@\n";
try eval {
Test2Sub();
print "Test2: \$@ = $@\n";
throw Ex::TestExceptionClass::Test2
'Ex::TestExceptionClass::Test2 thrown!';
};
print "Test2: \$@ = $@\n";
if(catch my $Ex) {
print("Caught ", ref($Ex), "\n");
if($Ex->isa('Ex::TestExceptionClass::Test2')) {
print($Ex->message, "\n");
} else {
print("Unexpected exception!\n");
print($Ex->message, "\n");
$Ex->rethrow();
}
}
print "Test2: \$@ = $@\n";
}
sub Test3 () {
print "Test3: \$@ = $@\n";
try eval {
throw Ex::TestExceptionClass::Test3::Ex1
'Ex::TestExceptionClass::Test3::Ex1 thrown!';
};
print "Test3: \$@ = $@\n";
try eval {
throw Ex::TestExceptionClass::Test3::Ex2
'Ex::TestExceptionClass::Test3::Ex2 thrown!';
};
print "Test3: \$@ = $@\n";
try eval {
die "Died for some reason!";
};
print "Test3: \$@ = $@\n";
try eval {
throw Ex::TestExceptionClass::Test3::Ex3
'Ex::TestExceptionClass::Test3::Ex3 thrown!';
};
print "Test3: \$@ = $@\n";
}
sub ExitProgram ($) {
my $ExitCode = shift;
if($ExitCode == 0) {
print "$0 finished successfully.";
} else {
print "$0 finished with exit code $ExitCode.";
}
exit($ExitCode);
}
__END__;
Dear David,
At first: Many thanks for your excellent extension to Exception::Class.
However, I have a few questions about TryCatch's philosophy.
I added a perl script to demonstrate, how I intend to use TryCatch
module. But some of my ideas doesn't seem to be fully compatible with
TryCatch's philosophy.
Example 1:
According documentation: "The exception is either popped from a hidden
error stack (see try) or, if the stack is empty, taken from the current
value of $@."
My confusion starts after catch() caught (and popped) the thrown
exception from the error stack. The copy of this exception is still in
$@ and the next catch() catches the same 'exception' again. The next
point is, that $@ isn't "popped" from this "error stack".
*From this example I would expect, that the content of $@ is ignored by
catch(), because it's just a copy of the exception on the error stack.
*
Example 2:
This example is the same as the example above, but a simple die is used
instead of a throw(). Again $@ isn't "cleaned" by catch(), so I have to
do this manually, to avoid a repeated catch of the same message. But
it's difficult to decide, whether $@ must be reset or not, because I
can't notice whether it caught an exception or a $@ entry. Even from the
exception class (Exception::Class::Base) it can't be concluded for sure,
what was the source of the caught exception.
*I suggest to change the class of "$@ exceptions" to something unique
like "Exception::Unexpected::Die" (if "thrown" by a die).*
*As a consequence from example 1 and 2 I feel, that the approach to use
the error stack AND $@ for catch(), leads easily to confusing
situations. I think, it would be clearer, that catch() ignores $@ and
try() pushes $@ entries to the error stack as every other exceptions
with a special exception class.
*
Example 3:
This example shows, that I possibly throw an exception in a subroutine,
that is processed outside the subroutine.
Works fine!
Example 4:
This example shows how an "unexpected" exception is passed upwardly, if
it couldn't be handled inside the subroutine.
Works fine!
Note:
If you uncomment the try eval {} in Test2Sub(), I expected to catch the
first exception from Test2Sub() in the main part. But it seems to be gone.
*Where is it?*
Example 5:
In this example a couple of exceptions are thrown in a subroutine, that
should be handled outside all at once. (One possible idea is to clean up
the error stack at program end and exit with an error code.)
*Here I miss something to avoid the "if(catch) - try eval { rethrow } -
while(catch)" construct. I suggest - according to "scalar(@error_stack)"
- a function like "ExceptionsOnStack()". This would allow a simpler
approach like "while(ExceptionsOnStack()) { ... }".*
To conclude, I want to confirm again, that TryCatch module is very
useful for me and - please - my remarks shouldn't be taken as a critique!
Maybe, I have the wrong view on how the module should be used. So, I
would be really thankful for clarification.
Many thanks in advance and
kind regards,
Oliver