2010-01-29 22 views
28

Una discusión en another question me hizo preguntarme: ¿qué tienen los sistemas de excepción de otros lenguajes de programación que Carece de Perl?¿Qué pasa con las excepciones en Perl?

excepciones incorporadas de Perl son un poco ad-hoc en que eran, al igual que el sistema de objetos de Perl 5, clase-de atornillados en el último momento, y sobrecargan otras palabras clave (eval y die), que se no dedicado específicamente a excepciones.

La sintaxis puede ser un poco fea, en comparación con los idiomas con la sintaxis integrada try/throw/catch tipo. Usualmente lo hago de esta manera:

eval { 
    do_something_that_might_barf(); 
}; 

if (my $err = [email protected]) { 
    # handle $err here 
} 

Existen varios módulos de CPAN que proporcionan azúcar sintáctico para añadir palabras clave try/catch y para permitir que la declaración fácil de jerarquías de clases de excepción y otras cosas.

El principal problema que veo con el sistema de excepción de Perl es el uso del especial [email protected] global para contener el error actual, en lugar de un mecanismo de tipo catch dedicado que podría ser más seguro, desde una perspectiva de alcance, aunque nunca personalmente se encuentra con cualquier problema con [email protected] siendo mordido.

+14

Tal vez las personas que al igual que otros lenguajes de programación hacen más errores. – mob

+6

Oh mierda, ¿acabo de decir eso en voz alta? ¡Es una broma! – mob

+4

Bueno, otros idiomas tienen excepciones y Perl no. Esa es la diferencia. Que los falsifiquemos no hace que Perl realmente tenga excepciones. –

Respuesta

13

Algunas clases de excepciones, p. Ej. Error, no puede manejar el control de flujo desde dentro de los bloques try/catch. Esto conduce a errores sutiles:

use strict; use warnings; 
use Error qw(:try); 

foreach my $blah (@somelist) 
{ 
    try 
    { 
     somemethod($blah); 
    } 
    catch Error with 
    { 
     my $exception = shift; 
     warn "error while processing $blah: " . $exception->stacktrace(); 
     next; # bzzt, this will not do what you want it to!!! 
    }; 

    # do more stuff... 
} 

La solución es utilizar una variable de estado y comprobar que fuera del bloque try/catch, que me parece horrible como el código n00b apestoso. otros

dos "trampas" en el error (ambos de los cuales me han causado dolor que están horrible para depurar si no ha funcionado en esto antes):

use strict; use warnings; 
try 
{ 
    # do something 
} 
catch Error with 
{ 
    # handle the exception 
} 

Parece sensato, ¿verdad? Este código compila, pero conduce a errores extraños e impredecibles. Los problemas son:

  1. use Error qw(:try) se omitió, por lo que se misparsed el bloque try {}... (que puede o no puede ver un aviso, dependiendo del resto de su código)
  2. falta punto y coma después del bloque catch!Poco intuitivos como el control de los bloques no utilizan puntos y comas, pero en realidad es una llamada trymétodo prototipo.

Oh sí, eso me recuerda también que debido a try, catch etc, son llamadas a métodos, eso significa que la pila de llamadas dentro de esos bloques no sea el esperado. (En realidad hay dos niveles de pila adicionales debido a una llamada interna dentro Error.pm.) Por lo tanto, tengo un par de módulos completos de código repetitivo como este, que sólo se suma el desorden:

my $errorString; 
try 
{ 
    $x->do_something(); 
    if ($x->failure()) 
    { 
     $errorString = 'some diagnostic string'; 
     return;  # break out of try block 
    } 

    do_more_stuff(); 
} 
catch Error with 
{ 
    my $exception = shift; 
    $errorString = $exception->text(); 
} 
finally 
{ 
    local $Carp::CarpLevel += 2; 
    croak "Could not perform action blah on " . $x->name() . ": " . $errorString if $errorString; 
}; 
+0

Todos los puntos excelentes. Especialmente el elemento de control de flujo. No había considerado eso antes. – friedo

+3

Al infractor: Agradecería sugerencias para mejorar este código feo, me ofende también. He cambiado a TryCatch en proyectos posteriores, pero esta publicación tenía la intención de demostrar los peligros con una clase de excepción particular (y puede extenderse a otros, no los he probado todos). – Ether

1

En C++ y C#, puede definir los tipos que se pueden lanzar, con bloques catch independientes que gestionan cada tipo. Los sistemas de tipo Perl tienen ciertos problemas molestos relacionados con RTTI y herencia, según lo que leo en el blog de chomatic.

No estoy seguro de cómo otros lenguajes dinámicos manejan excepciones; tanto C++ como C# son lenguajes estáticos y eso conlleva una cierta potencia en el sistema de tipos.

El problema filosófico es que las excepciones de Perl 5 están atornilladas; no están construidos desde el comienzo del diseño del lenguaje como algo integral de cómo se escribe Perl.

+0

Las restricciones de tipo no son tipos. Perl no tiene ningún problema con los tipos, porque no tiene ningún tipo. – jrockway

1

Ha pasado mucho tiempo desde que utilicé Perl, por lo que mi memoria puede ser borrosa y/o Perl puede haber mejorado, pero por lo que recuerdo (en comparación con Python, que uso a diario):

  1. desde excepciones son una adición tardía, que no son compatibles constantemente en las bibliotecas del núcleo

    (no es cierto, ya que no cuentan con el apoyo consistente en bibliotecas del núcleo debido a que los programadores que escribieron esas bibliotecas no les gusta excepciones.)

  2. no hay una jerarquía predefinida de excepciones - no se puede coger un grupo relacionado de excepciones por la captura de la clase base

  3. no existe un equivalente del intento: ... por último: ... para definir el código que se llamará independientemente de si se generó o no una excepción, por ej. para liberar recursos.

    (finally en Perl es en gran medida innecesaria - destructores de objetos Ejecutar inmediatamente después de la salida alcance; no siempre sucede que hay presión de memoria, porque se puede anular la planificación de los recursos no-memoria en su destructor, y funcionará. cordialmente)

  4. (por lo que yo puedo decir) sólo se puede tirar cuerdas -.. No se puede tirar objetos que tienen información adicional

    (completamente falsa die $object funciona igual de bien que die $string)

  5. no puede obtener un seguimiento de pila que muestra dónde se emitió la excepción - en Python se obtiene información detallada que incluye el código fuente para cada línea de la pila de llamadas

    (False. perl -MCarp::Always y disfrutar.)

  6. es un kludge a tope-feo.

    (. Subjetivo se implementa de la misma manera en Perl como lo es en todas partes Es sólo utiliza palabras clave con nombres diferentes..)

+3

# 5 no es cierto - véase 'Carp :: clica()' y 'Carp :: confesar()', y no es mucha molestia para establecer un '$ SIG {__ DIE __}' y '$ SIG {__ WARN __}' manejador con Carp para obtener rastros de pila por defecto. Pero el # 7 es más cierto, por lo que se nivela. – mob

+2

Re: # 4, puede lanzar objetos desde Perl 5.6 al menos (la mayoría de los sistemas de excepción en CPAN se basan en esto). Estoy de acuerdo en que la semántica 'finally' puede ponerse peluda – friedo

+0

# 3 - simplemente ponga este código después de eval, o use algo como Error.pm o Try :: Tiny. –

2

con Perl, lenguaje y excepciones escritos por el usuario se combinan: ambos establecen [email protected]. En otros idiomas, las excepciones de idioma son independientes de las excepciones escritas por el usuario y crean un flujo completamente independiente.

Puede ver la base de excepciones escritas por el usuario.

Si hay My::Exception::one y My::Exception::two

if ([email protected] and [email protected]>isa('My::Exception')) 

cogerá ambos.

Recuerde capturar cualquier excepción para no usuarios con un else.

elsif ([email protected]) 
    { 
    print "Other Error [email protected]\n"; 
    exit; 
    } 

También es bueno para envolver la excepción en una subllamada al submarino para lanzarla.

+1

Hola, parece nuevo aquí. Su respuesta no parece ser una respuesta al OP (qué está roto sobre las excepciones de Perl), sino un comentario sobre la respuesta de otra persona (posiblemente la de Dave Kirby). Como tal, aunque la información que presenta es útil en general, ya que no es una respuesta al OP, por lo que debería haberse publicado como un comentario o en otro lugar. –

+2

@ Adam Bellaire: Entendido. He editado mi respuesta. – bitbucket

23

El método típico de la mayoría de la gente ha aprendido a manejar excepciones es vulnerable a que faltan excepciones atrapados:

eval { some code here }; 
if([email protected]) { handle exception here }; 

Usted puede hacer:

eval { some code here; 1 } or do { handle exception here }; 

Esto protege de falta excepción debido a [email protected] siendo Demolí , pero aún es vulnerable a perder el valor de [email protected].

Para asegurarse de no criticar una excepción, cuando realice su evaluación, debe localizar [email protected];

eval { local [email protected]; some code here; 1 } or do { handle exception here }; 

Ésta es toda la rotura sutil, y la prevención requiere una gran cantidad de texto modelo esotérico.

En la mayoría de los casos esto no es un problema. Pero me he quemado por la excepción de comer destructores de objeto en código real. La depuración del problema fue horrible.

La situación es claramente mala. Mire todos los módulos en CPAN construidos proporcionan un manejo de excepción decente.

Respuestas abrumadoras a favor de Try::Tiny combinadas con el hecho de que Try :: Tiny no es "demasiado inteligente a la mitad", me han convencido para probarlo. Cosas como TryCatch y Exception::Class::TryCatch, Error, y así sucesivamente son demasiado complejas para que pueda confiar. Try :: Tiny es un paso en la dirección correcta, pero todavía no tengo una clase de excepción ligera para usar.

24

Try::Tiny (o módulos construidos sobre él) es la única forma correcta de tratar las excepciones en Perl 5. Los problemas involucrados son sutiles, pero el artículo vinculado los explica en detalle.

Así es como se usa:

use Try::Tiny; 

try { 
    my $code = 'goes here'; 
    succeed() or die 'with an error'; 
} 
catch { 
    say "OH NOES, YOUR PROGRAM HAZ ERROR: $_"; 
}; 

eval y [email protected] son piezas móviles que no necesita preocuparse con.

Algunas personas piensan que esto es un error, pero habiendo leído las implementaciones de otros lenguajes (así como Perl 5), no es diferente a cualquier otro. Solo existe la pieza móvil [email protected] que puede atrapar su mano ... pero como con otras piezas de maquinaria con partes móviles expuestas ... si no lo toca, no le arrancará los dedos. Entonces use Try :: Tiny y mantenga su velocidad de tipeo;)

+4

Tenga en cuenta que las versiones de Perl> = 5.14 ahora intenta mantener las excepciones en su sano juicio, por lo que esta ya no es la "única forma correcta". Pero todavía es bastante bueno. – jrockway

8

Un problema que encontré recientemente con el mecanismo de excepción eval tiene que ver con el controlador $SIG{__DIE__}. He asumido, erróneamente, que este controlador solo se llama cuando el intérprete de Perl sale por die() y quería utilizar este controlador para registrar eventos fatales. Luego resultó que estaba registrando excepciones en el código de la biblioteca como errores fatales que claramente estaban equivocados.

La solución fue para comprobar el estado de la variable $^S o $EXCEPTIONS_BEING_CAUGHT:

use English; 
$SIG{__DIE__} = sub { 
    if (!$EXCEPTION_BEING_CAUGHT) { 
     # fatal logging code here 
    } 
}; 

El problema que veo aquí es que el manejador de __DIE__ se utiliza en dos situaciones similares, pero diferentes. Esa variable $^S parece un add-on tardío para mí. Sin embargo, no sé si este es realmente el caso.

0

No utilice excepciones para errores regulares Solo los problemas fatales que detendrán la ejecución actual deberían morir. Todos los demás deben manejarse sin die.

Ejemplo: validación de parámetros de llamada sub: no mueren en el primer problema. Compruebe todos los otros parámetros y luego decida detenerse devolviendo algo o avisando y corrigiendo los parámetros defectuosos y proceda. Que hacer en modo prueba o desarrollo. Pero posiblemente die en modo de producción. Deja que la aplicación decida esto.

JPR (mi ingreso en el CPAN)

Saludos desde Sögel, Alemania