2009-07-14 3 views
4

Also spiele ich mit etwas schwarzer Magie in Perl (schließlich machen wir alle :-) und ich bin etwas verwirrt darüber, wie genau ich das alles machen soll. Hier ist, was ich beginne mit:Wie erstelle ich eine In-Memory-Klasse und schließe sie dann in Perl ein?

use strict; 
use warnings; 
use feature ':5.10'; 
my $classname = 'Frew'; 
my $foo = bless({ foo => 'bar' }, $classname); 
no strict; 
*{"$classname\::INC"} = sub { 
     use strict; 
     my $data = qq[ 
     package $classname 
     warn 'test'; 
     sub foo { 
      print "test?"; 
     } 
     ]; 
     open my $fh, '<', \$data; 
     return $fh; 
    }; 
use strict; 
unshift @INC, $foo; 
require $foo; 
use Data::Dumper; 
warn Dumper(\@INC); 
$classname->foo; 

bekomme ich folgende Fehler (je nachdem, ob meine erfordern Leitung kommentiert out):

Mit erfordern:

Recursive call to Perl_load_module in PerlIO_find_layer at crazy.pl line 16. 
BEGIN failed--compilation aborted. 

ohne:

Alle Zauberer, die einige dieser schwarzen Magie bereits kennen: Bitte antworten! Ich würde gerne mehr über diese Arcana lernen :-)

Auch Hinweis: Ich weiß, dass ich diese Art von Sachen mit Moose und anderen leichteren Helper-Modulen tun kann, versuche ich hauptsächlich zu lernen, also Empfehlungen zu verwenden -and-solches Modul wird nicht meine Stimmen bekommen :-)

aktualisieren: Ok, ich glaube, ich war nicht ganz klar, ursprünglich mit meiner Frage. Ich möchte im Grunde eine Perl-Klasse mit einer Zeichenfolge (die ich manipulieren und Interpolation in) basierend auf einer externen Datenstruktur erstellen. Ich stelle mir vor, dass das, was ich hier habe (sobald es funktioniert), nicht zu schwer sein sollte.

+3

Aus Neugier was willst du eigentlich erreichen? Um es anders auszudrücken, warum willst du das machen? – Telemachus

+0

@Telemachus: Nun, der Hauptgrund ist zu lernen, aber ich versuche auch einige Klassen basierend auf JSON zu generieren. Ich werde wahrscheinlich darüber in meinem Blog schreiben, sobald ich es laufen lasse. –

Antwort

9

Hier ist eine Version, die funktioniert:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $class = 'Frew'; 

{ 
    no strict 'refs'; 
    *{ "${class}::INC" } = sub { 
     my ($self, $req) = @_; 
     return unless $req eq $class; 
     my $data = qq{ 
      package $class; 
      sub foo { print "test!\n" }; 
      1; 
     }; 
     open my $fh, '<', \$data; 
     return $fh; 
    }; 
} 

my $foo = bless { }, $class; 
unshift @INC, $foo; 

require $class; 
$class->foo; 

Der @INC Haken den Namen der Datei bekommt (oder String require bestanden) als zweites Argument, und es wird jede Zeit genannt Es gibt eine oder use. Sie müssen also überprüfen, ob wir versuchen, $classname zu laden, und alle anderen Fälle ignorieren. In diesem Fall läuft Perl weiter unter @INC. Alternativ können Sie den Haken am Ende der @INC setzen. Dies war die Ursache für Ihre Rekursionsfehler.

ETA: IMHO, wäre ein viel besserer Weg, dies zu erreichen, einfach die Symboltabelle dynamisch zu erstellen, anstatt Code als String zu generieren. Zum Beispiel:

no strict 'refs'; 
*{ "${class}::foo" } = sub { print "test!\n" }; 
*{ "${class}::new" } = sub { return bless { }, $class }; 

my $foo = $class->new; 
$foo->foo; 

Keine use oder require ist notwendig, noch Messing mit bösen @INC Haken.

+0

Der mysteriöse Crash, den ich erlebt habe, als ich dachte, ich hätte das Äquivalent deines Codes ausprobiert, ist weg ... Weiß nicht, was verursacht hat. Anyway +1 für die richtige Lösung des Problems und betont, dass dies für den vom OP angegebenen Zweck sehr unnötig ist. Ich werde meine Antwort löschen. –

-3

Eine Perl-Klasse ist wenig mehr als eine Datenstruktur (normalerweise ein Hashref) , die in ein Paket gesegnet wurde, in dem eine oder mehrere Klassen Methoden definiert sind.

Es ist sicherlich möglich, mehrere Paket-Namespaces in einer Datei zu definieren; Ich sehe nicht, warum dies nicht möglich wäre in einem eval Konstrukt , das zur Laufzeit kompiliert wird (siehe perlfunc für die zwei verschiedenen eval Formulare).

#!/usr/bin/perl 

use 5.010; 
use strict; 
use warnings; 
use Data::Dumper; 

eval q[ 
    package Foo; 
    sub new { 
     my ($class, %args) = @_; 
     my $self = bless { %args }, $class; 
     return $self; 
    } 
    1; 
]; 
die [email protected] if [email protected]; 

my $foo = Foo->new(bar => 1, baz => 2) or die; 

say Dumper $foo; 
+0

Perl hat nicht wirklich Klassen (nur Namespaces), und Sie haben das mit Instanzen verwechselt. –

+0

Oh gut. Lasst uns einfach "wenig" durch "nichts" ersetzen. – hillu

6

ich dies tun:

use MooseX::Declare; 

my $class = class { 
    has 'foo' => (is => 'ro', isa => 'Str', required => 1); 
    method bar() { 
     say "Hello, world; foo is ", $self->foo; 
    } 
}; 

Dann können Sie $ Klasse wie jede andere metaclass verwenden:

my $instance = $class->name->new(foo => 'foo bar'); 
$instance->foo; # foo-bar 
$instance->bar; # Hello, world; foo is foo-bar 

usw.

Wenn Sie möchten, dass Sie dynamisch generieren Klassen zur Laufzeit, Sie müssen die richtige Metaklasse erstellen, sie instanziieren und dann die Metaklasseninstanz zum Generieren von Instanzen verwenden. Grundlegende OO. Class :: MOP kümmert sich um alle Details für Sie:

my $class = Class::MOP::Class->create_anon_class; 
$class->add_method(foo => sub { say "Hello from foo" }); 
my $instance = $class->new_object; 
... 

Wenn Sie es selbst zu tun, so dass Sie Ihre Zeit etwas Debuggen verschwenden, vielleicht versuchen:

sub generate_class_name { 
    state $i = 0; 
    return '__ANON__::'. $i++; 
} 

my $classname = generate_class_name(); 
eval qq{ 
    package $classname; 
    sub new { my \$class = shift; bless {} => \$class } 
    ... 
}; 

my $instance = $classname->new; 
+0

Ich mag diese Antwort am meisten, aber ich * bat * um keinen Elch :-) –

0

Für ein einfaches Beispiel wie man das macht, read the source of Class::Struct.

Wenn ich jedoch die Fähigkeit zum dynamischen Erstellen von Klassen für einen bestimmten Produktionscode benötigte, würde ich MooseX :: Declare betrachten, wie von jrockway vorgeschlagen.

Verwandte Themen