Perl's Exception::Class oder Exceptions klassifizieren

Zum Klassifizieren von Exceptions genügen virtuelle Klassen im Rahmen eines Trait

Betrachte untenstehenden Code einer typischen Anwendung, Exceptions werfen und fangen:

use strict;
use warnings;
use throw; # Trait

$, = "\n";
$\ = "\n";

# Mock Object
my $m = bless{};

# Exceptions werfen und auffangen
# Klassisch mit eval
eval{ die 123 }; # lost
eval{ $m->throw('ExA', 'Ex A: Invalid', 1) };
eval{ $m->throw('ExB', 'Ex B: Ungültig') };
eval{ die 234 }; # always overwrites $@

# Lookup for thrown exceptions
foreach my $xclass ($m->caught()){
    print do{
        my $e = $m->caught($xclass);
        $e->{error}, $e->{trace}, $@;
    };
}

# Modern Style
use Try::Tiny;

print try{
    $m->throw('MyEx','Invalid Handle!',1);
    2;
}
catch{
    if(my $e = $m->caught('MyEx')){
        $e->{error}, $e->{trace};
    }
};

Die Klassifizierung erfolgt, indem dem Wurf einer Exception der Name einer Klasse hinzugefügt wird. Später lassen sich damit bestimmte Exceptions gezielt abfragen. Im Grunde genommen gehts es ja nur darum, bestimmte Excepions wiederzufinden, dedizierte Klassen sind hierfür genausowenig erforderlich wie deren Instanzen.

Die Datei für den Trait

package UNIVERSAL;

# Trait for Handle Exceptions via virtual Exception Classes

use strict;
use warnings;

# Throws a classified exception and remembers the class as well
sub throw{
    my $self = shift;
    my $class = shift;
    my $msg = shift;

    my $trace = @_ ? do{
        require Devel::StackTrace;
        Devel::StackTrace->new(
            ignore_package => ['UNIVERSAL']
        );
    } : '';

    $self->{exceptions}{$class} = {
        error => $msg,
        trace => $trace
    };
    die "$msg\n";
}
# catchs either a certain or all Exceptions
sub caught{
    my $self = shift;
    my $class = shift;
    return $class ? $self->{exceptions}{$class} : keys %{$self->{exceptions}};
}
1;

Das Erbe von UNIVERSAL

Von dieser Klasse erben alle Klassen die es in Perl gibt und auch alle Klassen die in Perl erstellt werden. Methoden in einer Package mit diesem Namen sind also von Instanzen beliebig anderer Klassen nutzbar. Genau dieses Verhalten entspricht ja einer der hauptsächlichen Anforderungen eines Trait und natürlich auch der Anforderung des hier vorgestellten Traits: Daß der von Instanzen beliebiger Klassen genutzt werden kann.

Anmerkung zu eval, try, catch und $@

Wahrend eval oder try die eigentlichen Blöcke sind in denen eine Exception aufgefangen wird, dient catch dazu aufgefangene Exeptions wiederzufinden um entsprechend darauf reagieren zu können. Das hier vorgestellte Verfahren hebt die bisherige Beschränkung auf, daß in $@ nur die zuletzt gefallene Exception zu finden ist, denn sofern es mehrere eval{}-Blöcke gibt in denen Exceptions aufgetreten sind, wäre auf die herkömmliche Art und Weise $@ überschrieben worden. Durch die Klassifizierung jedoch lässt sich dieser Nachteil vermeiden.

Mit throw also wird eine klassifizierte Exception geworfen, mit try bzw. eval wird sie aufgefangen und mit catch wird sie eingefangen.

Perl's Exception::Class

Kurz und knapp: Die zur Benutzung beabsichtigten eigenen Exceptionklassen sind beim Einbinden der Basisklasse derselben bekanntzumachen:

use strict;
use warnings;
use Exception::Class qw(ExA ExB);

print eval{
    ExA->throw( error => 'Exeption for Class ExA' );
    1;
} || do{
    my $e = ExA->caught;
    $e, $e->trace;
};

Datenschutzerklärung: Diese Seite dient rein privaten Zwecken. Auf den für diese Domäne installierten Seiten werden grundsätzlich keine personenbezogenen Daten erhoben. Das Loggen der Zugriffe mit Ihrer Remote Adresse erfolgt beim Provider soweit das technisch erforderlich ist. s​os­@rolf­rost.de.