++ed by:
MISHIN EXPLORER SALVA
6 non-PAUSE users
Author image Enrique Nell
and 1 contributors

NAME

perlpragma - Crear un pragma de usuario

DESCRIPCIÓN

Un pragma (como strict o warnings) es un módulo que influye en algún aspecto del tiempo de compilación o de ejecución en Perl. Antes de Perl 5.10 solo podíamos usar los pragmas predefinidos; ahora podemos crear pragmas de usuario personalizados para modificar el comportamiento de funciones de usuario en un ámbito léxico.

Ejemplo básico

Supongamos que vamos a crear una clase que implementa operadores matemáticos sobrecargados y queremos proporcionar nuestro propio pragma, que funcione de manera parecida a use integer;. Lo llamaremos entero. Queremos que el código siguiente:

    use MisOperadores;

    my $l = MisOperadores->new(1.2);
    my $r = MisOperadores->new(3.4);

    print "A: ", $l + $r, "\n";

    use entero;
    print "B: ", $l + $r, "\n";

    {
        no entero;
        print "C: ", $l + $r, "\n";
    }

    print "D: ", $l + $r, "\n";

    no entero;
    print "E: ", $l + $r, "\n";

devuelva el siguiente resultado:

    A: 4.6
    B: 4
    C: 4.6
    D: 4
    E: 4.6

Es decir, cuando use entero; esté activo debe modificarse el comportamiento predeterminado de las operaciones de suma forzando la conversión del resultado a un valor entero. Para restablecer el comportamiento predeterminado se usa no entero;

Una implementación mínima del paquete MisOperadores podría ser:

    package MisOperadores;
    use warnings;
    use strict;
    use entero();
    use overload '+' => sub {
        my ($l, $r) = @_;
        # Primer barrido para comprobar un nivel de llamada superior desde aquí
        if (entero::in_effect(1)) {
            int($$l) + int($$r);
        } else {
            $$l + $$r;
        }
    };

    sub new {
        my ($clase, $valor) = @_;
        bless \$valor, $clase;
    }

    1;

Cargamos el pragma de usuario entero con una lista vacía () para impedir que se llame a su método import.

La interacción con la compilación de Perl tiene lugar dentro del paquete entero:

    package entero;

    use strict;
    use warnings;

    sub import {
        $^H{"entero/in_effect"} = 1;
    }

    sub unimport {
        $^H{"entero/in_effect"} = 0;
    }

    sub in_effect {
        my $nivel = shift // 0;
        my $hash_pistas = (caller($nivel))[10];
        return $hash_pistas->{"entero/in_effect"};
    }

    1;

Los pragmas se implementan como cualquier módulo, por lo que use entero; se convierte en:

    BEGIN {
        require entero;
        entero->import();
    }

y no entero; se convierte en:

    BEGIN {
        require entero;
        entero->unimport();
    }

Así, las rutinas import y unimport se llaman en tiempo de compilación para el código del usuario.

Los pragmas de usuario almacenan su estado en el hash mágico %^H, por lo que se pueden usar estas dos rutinas para manipularlo. La información de estado de %^H se almacena en el árbol sintáctico de operaciones y se puede recuperar como información de solo lectura en tiempo de ejecución mediante caller(), en el índice 10 de la lista de resultados devueltos. En el pragma de ejemplo, la recuperación se encapsula en la rutina in_effect(), que usa como parámetro el número de marcos de llamadas que hay que subir para encontrar el valor del pragma en el script del usuario. La rutina usa caller() para determinar el valor que tenía $^H{"entero/in_effect"} cuando se llamó a cada línea del script del usuario y, por tanto, proporcionar la semántica correcta en la subrutina que implementa la suma sobrecargada.

Nomenclatura de claves

Solo hay un único hash %^H, pero puede haber muchos módulos que deseen usar su semántica de definición de ámbito. Para evitar conflictos, deben asegurarse de usar claves distintas en el hash. Por ello, se sigue la convención de que en un módulo deben usarse únicamente claves que empiecen por el nombre del módulo (el nombre del paquete principal) seguido de un carácter "/". A continuación de este prefijo de identificación de módulo, el resto de la clave queda a discreción del programador del módulo y puede incluir cualquier carácter. Por ejemplo, un módulo llamado Foo::Bar debe usar claves como Foo::Bar/baz o Foo::Bar/$%/_!. Los módulos que sigan esta convención no provocarán conflictos.

En el núcleo de Perl se usan unas cuantas claves de %^H que no siguen esta convención, ya que se definieron antes de que esta se estableciera. Las claves que sigan la convención no provocarán conflictos con las claves antiguas del núcleo.

Detalles de la implementación

El árbol sintáctico de operaciones se comparte entre hilos de ejecución. Esto significa que existe la posibilidad de que el árbol sintáctico de operaciones sobreviva al hilo de ejecución (y, por tanto, a la instancia del intérprete) que lo creó, por lo que no se pueden almacenar escalares auténticos de Perl en dicho árbol. En su lugar se usa una forma compacta que solo puede almacenar valores enteros (con signo o sin signo), cadenas o undef; las referencias y los valores de punto flotante se convierten en cadenas. Si necesita almacenar varios valores o estructuras complejas, debe serializarlas (por ejemplo, con pack). La eliminación de una clave de hash de %^H se registra y se puede distinguir de la existencia de una clave con valor undef mediante exists, de la manera habitual.

No intente almacenar referencias a estructuras de datos como enteros que se recuperan a través de caller y se vuelven a convertir, ya que esto no garantiza una ejecución segura. Los accesos se harían en la estructura sin bloquear (lo cual no es seguro para los escalares de Perl), por lo que la estructura acaba por perderse o hay que liberarla cuando finalice el hilo de ejecución que la creó; si le sobreviven otros hilos de ejecución, esto puede ocurrir antes de que se elimine la referencia al árbol sintáctico de operaciones.

TRADUCTORES

  • Joaquín Ferrero (Tech Lead)

  • Enrique Nell (Language Lead)