2011-09-29 18 views
15

¿Cuáles son los escollos comunes asociados con eval de Perl, que pueden hacer que elija utilizar un módulo como Try::Tiny?¿Cuáles son los inconvenientes más comunes al usar la evaluación de Perl?

+0

posible duplicado de [¿Por qué es '$ @ '¿no es confiable?] (http://stackoverflow.com/questions/3686857/why-is-untrustworthy), [¿Qué hay de malo en las excepciones en Perl?] (http://stackoverflow.com/questions/2165161/) – mob

+2

El único re De todos modos, no usaría el built-in si no está ejecutando una versión actual de Perl. – tchrist

+0

@mob - sí, esa parece ser la misma pregunta. – Hugh

Respuesta

25

de Perl viene en dos sabores, eval cadena y el bloque eval. La evaluación de cadena invoca al compilador para ejecutar el código fuente. La evaluación de bloque rodea el código ya compilado en un contenedor que detectará la excepción die. (cadena evalúa captura la excepción die también, así como cualquier error de compilación).

Try :: Tiny solo se aplica al formulario de bloqueo de eval, pero lo siguiente se aplica a ambos formularios.

Cada vez que llame al eval, cambiará el valor de [email protected]. Será '' si la evaluación tuvo éxito o el error fue capturado por la evaluación.

Esto significa que cada vez que llame a una evaluación, borrará cualquier mensaje de error anterior. Try::Tiny localiza la variable [email protected] para usted, de modo que una evaluación exitosa no borre el mensaje de una evaluación fallida anterior.

La otra trampa proviene del uso de [email protected] como el control para determinar si la evaluación fue exitosa. Un patrón común es:

eval {...}; 
if ([email protected]) { 
    # deal with error here 
} 

Esto se basa en dos supuestos, primero que todo mensaje de error [email protected] podría contener es un valor verdadero (por lo general es cierto), y que no existe un código entre el bloque eval y la sentencia if.

Visualmente, por supuesto, esto último es cierto, pero si el bloque eval crea un objeto, y ese objeto queda fuera del alcance después de la evaluación, se llamará al método DESTROY del objeto antes de la instrucción if. Si DESTROY pasa a llamar a eval sin localizar [email protected] y tiene éxito, entonces cuando se ejecuta su instrucción if, se borrará la variable [email protected].

La solución a estos problemas es:

my $return = do { 
    local [email protected]; 
    my $ret; 
    eval {$ret = this_could_fail(); 1} or die "eval failed: [email protected]"; 
    $ret 
}; 

romper esa línea separados por línea, el local [email protected] crea un nuevo [email protected] para el bloque do que impide clobbering valores anteriores. my $ret será el valor de retorno del código evaluado. En el bloque eval, se asigna $ret, y luego el bloque devuelve 1. De esta forma, pase lo que pase, si la evaluación tiene éxito, se devolverá verdadera, y si falla devolverá falsa. Depende de usted qué hacer en caso de falla. El código anterior simplemente muere, pero puede usar fácilmente el valor de retorno del bloque eval para decidir ejecutar otro código.

Dado que el conjuro anterior es un poco tedioso, se vuelve propenso a errores.El uso de un módulo como Try::Tiny lo aísla de esos posibles errores, a costa de unas pocas llamadas a función más por evaluación. Es importante saber cómo usar eval correctamente, porque Try::Tiny no lo ayudará si tiene que usar una evaluación de cadena.

+4

Solucionado en la versión actual. – tchrist

+0

Corrección pequeña: '$ @' en realidad será una cadena vacía en lugar de undef si no se lanzó ninguna excepción. –

+1

@Grant McLean => gracias, debería haber recordado, porque así es como mi respuesta habitual se ocupa de los errores: 'perl -wE 'dice eval, $ @ while <>' ' –

6

Además de las respuestas anteriores, yo añadiría ...

  • eval se ve afectada por la acción causando $SIG{__DIE__} manejador mundial a distancia.
  • Es fácil para un principiante confundir eval BLOCK y eval STRING, ya que parecen hacer lo mismo, pero uno es un agujero de seguridad.

Try :: Tiny tiene sus propios escollos, el más grande es que si bien se ve como un bloque, en realidad es una llamada de subrutina. Eso significa esto:

eval { 
    ...blah blah... 
    return $foo; 
}; 

y esto:

try { 
    ...blah blah... 
    return $foo; 
}; 

no hacen lo mismo. Estos se presentan en el CAVEATS section of the Try::Tiny docs. Dicho esto, lo recomendaría más de eval.

+2

¿Estás diciendo que' eval' es * todavía * inusualmente roto en 5.14? ** ¿En serio? ** Sería * extremadamente * decepcionante, porque sé que se trabajó mucho para tratar de hacer que "Try :: Tiny" quede obsoleto al solucionar los errores subyacentes que plagaban a 'eval'.Si ese esfuerzo falló, entonces hay un problema horrible porque 'Try :: Tiny' sigue siendo simplemente un módulo de CPAN, no un núcleo. Si no puede hacer un trabajo real y confiable con el núcleo, entonces esa es una situación inaceptable. – tchrist

+1

@tchrist Olvidó eso. Según lo entiendo, 5.14.0 resolvió una clase de errores relacionados con las interacciones entre $ @ y la destrucción de objetos y generalmente hizo 'eval {...}; if ($ @) {...} 'más confiable. Creo que eso resuelve 2 de las 3 cosas: Try :: Tiny fixes ... y el tercero (un falso $ @) es bastante improbable. Todavía deja los puntos que mencioné. Sería una buena función 5.16 detener el lanzamiento de '$ SIG {__ DIE __}' dentro de una evaluación. Y marque el drama, amigo. – Schwern

+0

Llamar 'eval STRING' a un" problema de seguridad "no es demasiado dramático; ni siquiera es verdad He usado 'eval STRING' desde que apareció por primera vez en Perl2 hace unos veintitrés años, puedo asegurarle que nunca he experimentado ningún supuesto" problema de seguridad "con él. Claro, los programadores tontos pueden hacer cosas tontas con eso, pero esto es cierto para casi cualquier cosa. Si usted existe en un mundo patológicamente paranoico, debe utilizar el modo de contaminación y/o compartimentos de seguridad. En un mundo normal, 'eval STRING' obtiene mucho trabajo útil; ver el programa clásico * rename *. – tchrist

0

El uso de la función eval en X11 aún podría no mantenerse activo.

El código es como

eval {  
    @win_arrays = GetWindowsFromPid($pid); 
}; 

El guión será retirado

X Error de solicitudes con error: ...

Cuestiones relacionadas