2013-08-22 9 views
6

Ist es möglich, alle gültigen Methoden für eine bestimmte Perl-Klasse zu erhalten?Ist es möglich, alle gültigen Methoden für eine bestimmte Perl-Klasse zu erhalten?

Ich versuche, die Symboltabelle einer Klasse zu manipulieren und alle Methoden zu erhalten. Ich fand heraus, dass ich die Subroutinen von den Nicht-Subroutinen über die $obj->can($method) trennen kann, aber das tut nicht genau, was ich denke, dass es tut.

Der folgende Code gibt:

subroutine, Property, croak, Group, confess, carp, File 

ist jedoch subroutine kein Verfahren, (nur ein Unterprogramm) und croak, confess und alle in mein Paket importiert wurden.

Was will ich wirklich auszudrucken ist:

Property,Group, File 

Aber ich nehme:

subroutine, Property,Group, File 

Unten ist mein Programm:

#! /usr/bin/env perl 

use strict; 
use warnings; 
use feature qw(say); 

my $sections = Section_group->new; 
say join ", ", $sections->Sections; 

package Section_group; 
use Carp; 

sub new  { 
    return bless {}, shift; 
} 

sub Add { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub Sections { 
    my $self    = shift; 

    my @sections; 
    for my $symbol (keys %Section_group::) { 
     next if $symbol eq "new"; # This is a constructor 
     next if $symbol eq "Add"; # Not interested in this method 
     next if $symbol eq "Sections";  # This is it's own method 
     push @sections, $symbol if $self->can($symbol); 
    } 

    return wantarray ? @sections : \@sections; 
} 

sub subroutine { 
    my $param1    = shift; 
    my $param2    = shift; 
} 

sub Group { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub File { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub Property { 
    my $self    = shift; 
    my $section    = shift; 
} 

Antwort

6

Das ist ziemlich trivial. Wir wollen nur die Subnamen beibehalten, die ursprünglich in unserem Paket definiert wurden. Jeder CV (Codewert) hat einen Zeiger auf das Paket, in dem er definiert wurde. Dank B, können wir das untersuchen:

use B(); 

... 

if (my $coderef = $self->can($symbol)) { 
    my $cv = B::svref_2object $coderef; 
    push @sections, $symbol if $cv->STASH->NAME eq __PACKAGE__; 
} 

# Output as wanted 

Das heißt, wir führen Selbstbeobachtung svref_2object verwenden. Dies gibt ein Perl-Objekt zurück, das eine interne Perl-Datenstruktur darstellt.

Wenn wir in einen Coderef schauen, erhalten wir einen B::CV object, der den internen CV darstellt. Das Feld STASH in einem Lebenslauf verweist auf den Stash, in dem es definiert wurde. Wie Sie wissen, ist ein Stash nur ein spezieller Hash (intern dargestellt als HV), so dass $cv->STASH einen B::HV zurückgibt. Das Feld NAME eines HV enthält den vollständig qualifizierten Paketnamen des Stash, wenn der HV ein Stash ist und kein regulärer Hash.

Jetzt haben wir alle Informationen, die wir brauchen, und können den gewünschten Paketnamen mit dem Namen des Stores der Coderef vergleichen.

Natürlich ist dies vereinfacht, und Sie werden durch @ISA für allgemeine Klassen recurse wollen.


Niemand mag verschmutzte Namespaces. Glücklicherweise gibt es Module, die fremde Symbole aus dem Versteck entfernen, z. namespace::clean. Dies ist kein Problem, wenn die CVs aller Subs, die Sie aufrufen, zur Kompilierungszeit bekannt sind.

+3

Dann wieder manchmal Leute importieren tatsächlich eine Methode aus einem anderen Paket (z. B. 'exporter 'import';' im Gegensatz zu '@ISA = qw (Exporter);'). – cjm

+0

@cjm Natürlich, aber ich denke, das passiert nicht oft, und kann sogar ein Antipattern sein. Hoffentlich haben die meisten Leute die prozedurale Programmierung (mit Importen) von OOP getrennt (was viele Namespace-Probleme löst). Es gibt keine Möglichkeit zu wissen, ob ein importiertes Sub eine Methode sein sollte, also muss das gut genug sein. (Warte, vielleicht, wenn wir auf das Attribut ': method' zugreifen können ...) – amon

+0

_Das ist ziemlich trivial_. Trivial? Sie bauen Tachyonen-Generatoren an den Wochenenden? Perldoc on ** svref_2object **: _Nimmt einen Verweis auf einen beliebigen Perl-Wert an und wandelt den referenzierten Wert in ein Objekt in der entsprechenden B :: OP-abgeleiteten oder B :: SV-abgeleiteten Klasse um._ Ich habe keine Ahnung was es spricht davon. Es funktioniert jedoch. Ich denke, es ist an der Zeit für mich, mich mit dem bisher Ungelösten zu beschäftigen und mein Perl-Spiel ein bisschen mehr zu verbessern. Entweder das, oder lerne Python. –

6

Was möchten Sie tun? Warum ist es wichtig, wie eine Klasse eine Methode definiert oder implementiert, auf die sie reagiert?

Perl ist eine dynamische Sprache, das bedeutet, dass Methoden überhaupt nicht existieren müssen. Mit AUTOLOAD kann eine Methode vollkommen in Ordnung und aufrufbar sein, aber nie in der Symboltabelle angezeigt werden. Eine gute Schnittstelle würde in diesen Fällen can funktionieren, aber es könnte Fälle geben, in denen eine Klasse oder ein Objekt sich dafür entscheidet, mit false zu antworten.

Das Modul Package::Stash kann Ihnen helfen, definierte Subroutinen in einem bestimmten Namespace zu finden, aber wie Sie sagen, sind sie möglicherweise nicht in derselben Datei definiert. Die Methoden in einer Klasse könnten aus einer geerbten Klasse stammen. Wenn Sie sich darum kümmern, woher sie kommen, tun Sie wahrscheinlich falsch.

+0

Ich habe eine Klasse namens _Sections_, die verschiedene Abschnittstypen in meiner Steuerdatei darstellt, die im Windows INI-Format ist. Jeder Abschnitt hat Parameter, aber die Parameter sind für jeden Abschnittstyp unterschiedlich. (Es sind derzeit fünf von ihnen). Jeder Abschnittstyp ist eine Unterklasse des Abschnitts. Ich habe eine andere Klasse, die alle verschiedenen Abschnittslisten für mich enthält. Jeder Abschnittstyp erhält in dieser letzten Klasse eine neue Methode. Auf diese Weise befindet sich meine gesamte Definition für diese INI-Datei in einem einzigen Objekt. Eine Methode für meine Klasse enthält die Namen aller anderen Unterklassen. –

+0

Ich benutzte AUTOLOAD, aber ich vermeide es jetzt. Mit 'use strict' wird die Programmierung verbessert. Die Verwendung von Referenzstrukturen beseitigt diese Sicherheit. Ich kann '$ foo -> {BAR}' an einem Ort haben und '$ foo -> {BRA}' in einem anderen, und strikt kann das nicht verstehen. OO bringt diese Sicherheit zurück. Wenn meine Methode $ foo-> Bar ist, wird der Aufruf von $ foo-> Bra mein Programm zum Absturz bringen. AUTOLOAD bricht dieses Design. Beide sind jetzt echte Methoden. Es führt auch zu einem schlechteren Gesamtdesign. Ich kann es mit AUTOLOAD flügeln. Ich muss die Dinge nicht durchdenken. Meine vorherige Version dieses Programms verwendet AUTOLOAD, und es kann schwierig sein, dank AUTOLOAD zu debuggen. –

+0

Ich würde all diese Erklärung zu der Frage hinzufügen :) –

Verwandte Themen