2011-01-12 9 views
5

Ho bisogno di rimuovere un metodo dalla tabella dei simboli Perl in fase di esecuzione. Ho provato a farlo usando undef &Square::area, che elimina la funzione, ma lascia alcune tracce dietro. In particolare, quando viene chiamato $square->area(), Perl si lamenta che "Non è un riferimento CODE" anziché "Subroutine non definita & Square :: area called", che è ciò che mi aspetto.Perché questo Perl produce "Non un riferimento CODICE?"

Si potrebbe chiedere "Perché è importante? Hai eliminato la funzione, perché la chiameresti?" La risposta è che non lo sto chiamando, Perl lo è. Square eredita da Rectangle e voglio che la catena di ereditarietà passi $square->area a &Rectangle::area, ma invece di saltare Square dove il metodo non esiste e quindi rientra nell'area di Rectangle(), la chiamata al metodo muore con "Not a CODE reference ".

Stranamente, questo sembra accadere solo quando & Square :: area è stata definita dall'assegnazione typeglob (ad esempio *area = sub {...}). Se la funzione viene definita utilizzando l'approccio standard sub area {}, il codice funziona come previsto.

Anche interessante, non definendo l'intero glob funziona come previsto. Non solo indefinendo la subroutine stessa.

Ecco un breve esempio che illustra il sintomo, e contrasta con il corretto comportamento:

#!/usr/bin/env perl 
use strict; 
use warnings; 

# This generates "Not a CODE reference". Why? 
sub howdy; *howdy = sub { "Howdy!\n" }; 
undef &howdy; 
eval { howdy }; 
print [email protected]; 

# Undefined subroutine &main::hi called (as expected) 
sub hi { "Hi!\n" } 
undef &hi; 
eval { hi }; 
print [email protected]; 

# Undefined subroutine &main::hello called (as expected) 
sub hello; *hello = sub { "Hello!\n" }; 
undef *hello; 
eval { hello }; 
print [email protected]; 

Aggiornamento: allora ho risolto questo problema utilizzando Package :: Stash (grazie @Ether), ma sono Sono ancora confuso dal motivo per cui sta accadendo in primo luogo. perldoc perlmod dice:

package main;

sub Some_package::foo { ... } # &foo defined in Some_package

Questa è solo una scorciatoia per un incarico typeglob in fase di compilazione:

BEGIN { *Some_package::foo = sub { ... } }

ma sembra che esso isn' t solo stenografia, perché i due causano un comportamento diverso dopo aver indefinito la funzione. Sarei grato se qualcuno potesse dirmi se si tratta di (1) documenti errati, (2) bug in perl o (3) PEBCAK.

+0

Dove hai avuto l'idea che 'undef & Square :: area' avrebbe eliminato una subroutine? Non un attacco, solo curioso ... – Ether

+3

Da 'perldoc -f undef'. L'esempio che danno è 'undef &mysub;', ma funziona anche con nomi qualificati. – KingPong

+1

Alternate domande "potreste chiedere". Perché hai definito & Square :: area in primo luogo? Perché non [re] definisci & Square :: area da delegare alla sua superclasse? –

risposta

7

La manipolazione dei riferimenti alle tabelle dei simboli è destinata a metterti nei guai, poiché ci sono molte piccole cose difficili da correggere. Fortunatamente c'è un modulo che fa tutto il lavoro pesante per te, Package::Stash - quindi chiama i suoi metodi add_package_symbol e remove_package_symbol se necessario.

Un altro buon metodo di installazione che si potrebbe voler verificare è Sub::Install - particolarmente utile se si desidera generare molte funzioni simili.

Quanto al perché il tuo approccio non è corretta, diamo uno sguardo alla tabella dei simboli dopo aver cancellato il codice di riferimento:

sub foo { "foo!\n"} 
sub howdy; *howdy = sub { "Howdy!\n" }; 

undef &howdy; 
eval { howdy }; 
print [email protected]; 

use Data::Dumper; 
no strict 'refs'; 
print Dumper(\%{"main::"}); 

stampe (in forma abbreviata):

 
    $VAR1 = { 
       'howdy' => *::howdy, 
       'foo' => *::foo, 
    }; 

Come si può vedere , lo slot 'howdy' è ancora presente - non definendo lo &howdy in realtà non è sufficiente fare . È necessario rimuovere esplicitamente lo slot glob, *howdy.

+0

Grazie per il puntatore a Package :: Stash. Ho perso in qualche modo remove_package_symbol l'ultima volta che ho guardato. Questo è quello di cui ho bisogno, al contrario di remove_package_glob, che potrebbe anche clobare @area e $ area, ecc. Ma mentre questa è un'informazione utile, non risponde alla domanda, ma dice "è una domanda difficile, non preoccuparsi di chiedere ". – KingPong

+0

@KingPong: ricarica la pagina; la spiegazione ha richiesto un po 'più tempo per scrivere. :) – Ether

+0

@Esther: Se tu "undef & foo', vedrai che è ancora nella tabella dei simboli proprio come howdy(), anche se non lo hai fatto. La differenza è che Perl sa che foo() se n'è andato, presumibilmente perché lo hai dichiarato con "sub foo {...}", che, secondo 'perldoc perlmod' è equivalente a' BEGIN {* foo = sub {.. .}} '. – KingPong

2

La ragione per cui si verifica è proprio perché è stato assegnato un typeglob.

Quando si cancella il simbolo CODE, il resto di typeglob è ancora sospeso, quindi quando si tenta di eseguire howdy si punterà sul tipo non typeglob CODE.

Problemi correlati