Klassen dynamisch erzeugen
sven
- perl
Hi,
Die Kurzversion:
Wie kann ich in Perl
a) Klassen dynamisch zur Laufzeit erzeugen,
b) anschl. mit einer festen Superklasse verknüpfen (@ISA)
c) und weiters einige Eigenschaften abändern (Constanten hinzufügen)?
Entweder, ich suche
Jedenfalls weiß ich nicht mehr weiter. Bin also dankbar für alle Hinweise!
danke und salut
Sven
Hier die Langversion zur Info:
Vielleicht gehe ich ja auch nur ein einfaches Problem, viel zu kompliziert an:
Ich verwende den tie-Befehl in Perl um Tabellenfelder aus einer MySQL-DB in einer FastCGI-Anwendung abzufragen und zu schreiben. Das ist sehr schön, komfortabel.
Ich brauche also nicht mühsam SELECTS und UPDATES durchführen, a la "UPDATE tabelle SET WERT=wert WHERE ID=x"
sondern ich schreibe einfach den neuen Wert per
$WERT = wert;
und die Perlklasse, die mit dem tie an die Variable gebunden wurde macht im Hintergrund den Rest.
Mein Problem ist jetzt allerdings, dass der Speicherverbrauch mit größeren Tabellen unnötig ansteigt.
Denn derzeit speichere ich z.B. bei jeder Instanz den Feldbezeichner (z.B. ID, PARENT, ...), obwohl dieser bei allen Feldern derselben Spalte ja gleich ist.
Ich könnte also hergehen und jeweils eine TIE-Klasse für ID, PARENT, ... usw. von einer Superklasse ableiten und nur mit einer anderen Konstante für den Feldbezeichner versehen. Die Konstante ist also nur einmal in der Klasse gespeichert, anstatt vielfach in jeder Instanz!
Das Problem ist nur, dass sich in einem anderen Bereich der Software bestimmte Feldbezeichner aufgrund der Konfiguration von Server zu Server unterscheiden. Sprich die Konfiguration bestimmt erst zur Laufzeit, wie diese Feldbezeichner/Konstanten heißen!
Daher kann ich die verschiedenen Klassen nicht statisch erstellen, sondern müsste sie zu Laufzeit dynamisch in Abhängigkeit der Konfiguration generieren. Daher eben meine Frage.
a) Klassen dynamisch zur Laufzeit erzeugen,
b) anschl. mit einer festen Superklasse verknüpfen (@ISA)
c) und weiters einige Eigenschaften abändern (Constanten hinzufügen)?
Für ganz andere Lösungen bin ich natürlich auch sehr dankbar.
Hallo!
Wie kann ich in Perl
a) Klassen dynamisch zur Laufzeit erzeugen,
b) anschl. mit einer festen Superklasse verknüpfen (@ISA)
c) und weiters einige Eigenschaften abändern (Constanten hinzufügen)?
Ich hab zwar keine Antworten für dich aber ein paar Fragen. :-)
Geht das überhaupt mit irgendeiner Programmiersprache? Ich kenn Perl nicht so gut. Aber sprichst du hier von Klassen im objektorientiertem Sinn?
Auf die Idee Klassen dynamisch zur Laufzeit zu erzeugen bin ich noch nie gekommen.
mfg
frafu
Hallo,
Ich hab zwar keine Antworten für dich aber ein paar Fragen. :-)
Ich glaube nicht, dass der OP Folgefragen gerne hat :)
Geht das überhaupt mit irgendeiner Programmiersprache? Ich kenn Perl nicht so gut. Aber sprichst du hier von Klassen im objektorientiertem Sinn?
Ja. Mit Perl kann man auch objektorientiert programmieren.
Auf die Idee Klassen dynamisch zur Laufzeit zu erzeugen bin ich noch nie gekommen.
Ich auch nicht. Aber ich kann mir darunter auch nichts vorstellen.
Markus.
Hi,
Ich auch nicht. Aber ich kann mir darunter auch nichts vorstellen.
Immerhin hab ich gerade das auf search.cpan.org gefunden - das hilft etwas bei der Vorstellung, denke ich:
use Perl6::Classes;
class Composer {
submethod BUILD { print "Giving birth to a new composer\n" }
method compose { print "Writing some music...\n" }
}
class ClassicalComposer is Composer {
method compose { print "Writing some muzak...\n" }
}
class ModernComposer is Composer {
submethod BUILD($) { $.length = shift }
method compose() { print((map { int rand 10 } 1..$.length), "\n") }
has $.length;
}
my $beethoven = new ClassicalComposer;
my $barber = new ModernComposer 4;
my $mahler = ModernComposer->new(400);
$beethoven->compose; # Writing some muzak...
$barber->compose # 7214
compose $mahler; # 89275869347968374698756....
Leider ist das Ding Beta und in Entwicklung. Also vielleicht nicht unbedingt für eine Produktionsmachine ;(
Bin also immer noch dankbar für andere Wege.
danke
Sven
Hi,
Ich hab zwar keine Antworten für dich aber ein paar Fragen. :-)
Besser als nichts *g*
Geht das überhaupt mit irgendeiner Programmiersprache?
Gute Frage. Kann mir beides gut vorstellen.
Ich kenn Perl nicht so gut. Aber sprichst du hier von Klassen im objektorientiertem Sinn?
Ja! In Perl ist eine Klasse eigentlich ein Modul. Wenn man also Module (Namensräume) dynamisch erstellen könnte, wäre das vielleicht schon die Lösung.
Auf die Idee Klassen dynamisch zur Laufzeit zu erzeugen bin ich noch nie gekommen.
Ich auch bis jetzt nicht. Aber es ist sinnlos bei hunderten Felder hundertemale in der entsprechenden Instanz die Information 'PARENT' abzulegen.
Auch wenn ich das über eine Referenz immerhin schon platzsparender machen kann - es ist sinnlos!
Diese Information sollte schon in der Klasse stecken. Nur das ich diese Informationen eben erst zur Laufzeit zur Verfügung habe. Also wären dynamische Klassen hier eine Lösung.
Aber da hätte ich auch noch eine Frage:
Wieviel Platz brauch eine Refernz in Perl?
Ich überlege mir momentan die Ersparnis.
'PARENT' braucht netto 6Byte.
Eine Referenz braucht? 1Byte mindestens, ich denke sogar etwas mehr, oder?
Das bringt dann zwar etwas, aber der Speicherverbrauch skaliert immer noch der Feldzahl.
danke fürs Mitdenken.
Sven
Hallo,
#!/usr/bin/perl
use strict;
use warnings;
package palim;
{
use vars qw(@ISA);
sub new
{
bless({}, shift);
}
sub namespaceMe($)
{
push(@ISA, $_[1]);
}
sub doPalim()
{
print "palim\n";
}
}
# uiuiui, das ist ganz arg böse !!!!
eval << "PACK";
package blub;
{
sub new
{
bless({}, shift);
}
sub doBlub()
{
print "blub\n";
}
}
PACK
package main;
my $o = new palim();
$o->doPalim();
eval { $o->doBlub(); };
print ( ($@ =~ /(.*?)\ at\ /)[0] . "\n" ) if $@;
$o->namespaceMe('blub');
$o->doBlub();
gibt:
palim
Can't locate object method "doBlub" via package "palim"
blub
für:
v5.8.7 built for i386-freebsd-64int
gruss
Hi,
da kann ich ja noch viel lernen ;))
uiuiui, das ist ganz arg böse !!!!
Wie böse?
eval, war in meinen nächtlichen Überlegungen nun auch mein letzter Lösungsweg. Habe gehofft, dass das auch gescheiter geht.
Performancemäßig ist es ja kein Problem. Die evals werden nur beim Startup gebraucht und da habe ich kein performance-Problem (nachdem es sich ja um FastCGI handelt)
Der Interpreter scheint es auch zu schaffen. Sonst irgendwelche Problemherde?
package main;
my $o = new palim();
$o->doPalim();
eval { $o->doBlub(); };
print ( ($@ =~ /(.*?)\ at\ /)[0] . "\n" ) if $@;
$o->namespaceMe('blub');
$o->doBlub();
Das mit dem Namespace finde ich ja eine interessante Idee - muss ich mir merken.
Ich hab nur noch nicht ganz durchgeblickt, was es mir hier bringt.
Ich hätte einfach die Konstanten in ein Array gesetzt und in einer Schleife mit eval die packages definiert. Mehr als das jedes package von der Superklasse erbt und eine eigene Konstante definiert bekommt, hätte ich gar nicht gemacht.
vielen Dank jedenfalls.
Hallo,
Wie böse?
eval, war in meinen nächtlichen Überlegungen nun auch mein letzter Lösungsweg. Habe gehofft, dass das auch gescheiter geht.
eval "bäh";
eval { gut(); };
Der Interpreter scheint es auch zu schaffen. Sonst irgendwelche Problemherde?
s.o., bei eval "" musst du genau aufpassen was du machst!
wahrscheinlich geht das sehr auf die Kosten der Wartbarkeit und der Fehlerbehebung.
my $o = new palim();
$o->doPalim();
eval { $o->doBlub(); };
print ( ($@ =~ /(.*?)\ at\ /)[0] . "\n" ) if $@;
hier hab ich nur das "at line 65 [..] ./bla.pl" weggeschnitten, das sah so blöd aus.
$o->namespaceMe('blub');
$o->doBlub();
das war ein test, dass es auch funktioniert.
Das mit dem Namespace finde ich ja eine interessante Idee - muss ich mir merken.
welche andere möglichkeit hast du noch zu vererben ?!
Ich hätte einfach die Konstanten in ein Array gesetzt und in einer Schleife mit eval die packages definiert. Mehr als das jedes package von der Superklasse erbt und eine eigene Konstante definiert bekommt, hätte ich gar nicht gemacht.
naja so richtig hab ich das nicht verstanden. du bastelst dir ein package zusammen, dann willst du objekte daraus machen (logisch ^^):
my $packageName = 'neueKlasse';
eval << "PACK";
package $packageName;
use vars qw(\@ISA);
\@ISA = qw(deineUltraHochWichtigeBasisKlasse);
sub new()
{
my \$class = shift;
#Konstruktor der Basisklasse aufrufen, falls du das brauchst
bless ( (\$class)->SUPER::new(), \$class );
}
PACK
my $newObj = $packageName->new();
(hab ich jetzt nicht getestet, war zu faul)
vielen Dank jedenfalls.
bitte :D
gruss
Hi,
s.o., bei eval "" musst du genau aufpassen was du machst!
wahrscheinlich geht das sehr auf die Kosten der Wartbarkeit und der Fehlerbehebung.
Das wäre bei Perl6::Class genau dasselbe. Da ist einfach nur ein Filter vor den Interpreter geschaltet, der den Code umschreibt. Mit den Zeilenmeldungen hat man bei der Fehlersucher, sicher viel Spaß *grmpf*
Soweit ich weiß, ist bei eval {} auch der Vorteil, dass es gleich mitkompiliert werden kann, wohingegen eval "" erst zur Laufzeit interpretiert wird.
welche andere möglichkeit hast du noch zu vererben ?!
Ich kenn auch nur @ISA, aber brauche ich die Vererbung nicht dynamisch ändern. Lediglich die Klassen (die alle auf dieselbe Basisklasse zurückgeführt werden) sollen dynamisch erstellt werden.
my $packageName = 'neueKlasse';
eval << "PACK";
package $packageName;
use vars qw(@ISA);
@ISA = qw(deineUltraHochWichtigeBasisKlasse);sub new()
{
my $class = shift;
#Konstruktor der Basisklasse aufrufen, falls du das brauchst
bless ( ($class)->SUPER::new(), $class );
}
PACKmy $newObj = $packageName->new();
Genau, nur ich werde mir sogar die new-Funktion schenken. Auch die kommt von der Basisklasse. Lediglich eine Konstante wird in jeder Klasse extra definiert.
Inzwischen arbeite ich an einem anderen Lösungsweg. Hatte eigentlich damit gerechnet, dass das überhaupt nicht hinhaut, aber vielleicht taugt das etwas:
~~~perl
#!/usr/bin/perl
*test = *integ;
my $globRef = \*test;
$globRef::const = 'PARENT';
*test = *integ;
my $globRef2::const = 'ID';
print "integ:const ".$integ::const."\n";
print "globRef:const ".$globRef::const."\n";
print "globRef2:const ".$globRef2::const."\n";
$x = integ::new();
$y = *{$globRef}::new();
package integ;
sub new()
{
print "new instance of class".*__PACKAGE__{PACKAGE}__."\n";
return bless( {}, shift );
}
Naja, soweit schaut alles sehr vielversprechend aus. Die Constanten von $globRef und $globRef2 sind unterschiedlich, einzig der Aufruf der new Methode will mir über die globRefs nicht gelingen. Erst da bricht das Skript mit einer Fehlermeldung ab.
Ich muss gestehen, ich weiß da nicht wirklich ganz genau, was ich mache. Kannst Du vielleicht den letzten Schritt lösen.
Eine Funktion zu einer GlobRef muss man doch irgendwie aufrufen können, oder?
danke
Sven