Coderef innerhalb von new() weitergeben
Alex
- perl
Hi,
ich möchte gerne bei der Erzeugung eines Modules mit new() gleichzeitig eine Code-Referenz an ein anderes Modul übergeben.
sub new {
my ( $self, $Web ) = @_;
croak( EC_1 ) unless $Web;
my %config;
$config{ Web } = $Web;
my $self_ = bless( %config, $self );
$Web->add_Menu( sub { return $self_->main() }, [ "Item1", "Item2" ] );
#return bless( %config, $self );
return $self_;
}
Sowas in der Art, leider kann ich dann die Seite im Browser nur einmal aufrufen, beim zweiten mal lädt der unendlich lang.
Das ganze Funktioniert, wenn ich
return $self->main()
als anonyme Coderef übergebe, hat aber den Nachteil, daß ich das %Config noch nicht im $self habe und somit nicht das komplette Objekt besitze.
Meine Frage ist, ob jemand ne Ahnung hat wie man sowas realisiert, bzw was der Unterschied ist in folgenden Zeilen:
$self_ = bless( %config, $self ); return $self_;
return bless( %config, $self );
danke, Alex
Hab das Problem eingegrenzt und es liegt in der add_Menu Funktion:
Diese Funktion kapselt einfach nur die übergebenen Coderefs in einer Hashref.
@$Menu_Funs{ @menu_items } = ( $code_ref ) x scalar( @menu_items );
Damit wird zu jedem Menuitem die Codref gespeichert die aufgerufen wird. Ich kann auch n Objekt übergeben, würde mir auch genügen, aber:
Was ich allerdings nicht verstehe: Wieso dauert diese Funktion unendlich lang egal ob Coderef oder Objekt übergeben wird?
Übergebe ich anstelle von Coderefs einen Scalar "blabla" ist die Funktion flott wie nix.
alex
@$Menu_Funs{ @menu_items } = ( $code_ref ) x scalar( @menu_items );
sagen wir mal so, du hast einen sehr eigenwilligen Stil
Ich hab keine Ahnung ob du wirklich weißt was du da tust und ich frage mich warum du unbedingt versuchst den Code möglichst unleserlich zu machen. Perl ist relativ schnell und wird nicht wesentlich schneller durch solche undurchschaubare Konstruktionen.
Was ich allerdings nicht verstehe: Wieso dauert diese Funktion unendlich lang egal ob Coderef oder Objekt übergeben wird?
Läßt sich so nicht sagen, dein Code läßt sich leider auch nicht testen.
Struppi.
ok, dann frag ich theoretisch mal nach:
Du hast ein ModulA und Module 1..n.
ModulA soll eine Menustruktur aller Module enthalten, dazu dachte ich mir könnte jedes Modul doch praktischerweise in der new() Funktion
eine Funktion aus ModulA aufrufen um diese Menustruktur aufzubauen. Daher add_Menu().
Das Menu hat 2 Variablen ( Hashref und Arrayref ). In der Arrayref sind die sortierten Menuitems und in der Hashref sind die Menuitems als Keys und die Coderef als Values.
Jedes Modul 1..n übergibt nun eine Coderef und eine Arrayref im new().
Soviel zur Theorie
Wenn die Syntax oben zu unleserlich ist, dann eben diese:
for my $item ( @menu_items ) {
$Menu_Funs->{ $item } = $code_ref # oder Objekt
}
Ich restarte den Apache und das Script läßt sich laden. Die Module sind ordentlich registriert. Ich lade die Seite im Browser erneut und Perl hängt sich an der Stelle auf sofern es eine Coderef oder ein Objekt registrieren soll.
Das Anlegen der Arrayref in ModulA funktioniert einwandfrei, die Hashref ist also das Problem.
Achja, ich nutze mod_perl und werde das Gefühl nicht los, dass es sich um ein Cacheproblem oder einen Bug handelt.
Hast du ne bessere Idee wie ich das realisieren könnte?
Danke, Alex
ich möchte gerne bei der Erzeugung eines Modules mit new() gleichzeitig eine Code-Referenz an ein anderes Modul übergeben.
Soweit klar.
sub new {
my ( $self, $Web ) = @_;
croak( EC_1 ) unless $Web;
In $web soll deine Codereferenz stehen, richtig?
my %config;
$config{ Web } = $Web;
Und das soll dein Objekt werden, auch richtig?
my $self_ = bless( %config, $self );
Autsch. Verwende bitte nachvollziehbare Variablennamen. $self_ und $self sind zwei paar Schuhe.
$Web->add_Menu( sub { return $self_->main() }, [ "Item1", "Item2" ] );
Hier möchtest du den Code, der in $Web übergeben wurde, ausführen, richtig? Allerdings schreibst du, dass dies eine Codereferenz sei, die wird indes durch voranstellen eines '&' ausgeführt. Handelt es sich bei '$Web' vielleicht um ein Objekt?
Das ganze Funktioniert, wenn ich
return $self->main()
als anonyme Coderef übergebe, hat aber den Nachteil, daß ich das %Config noch nicht im $self habe und somit nicht das komplette Objekt besitze.
Was steht denn in %Config?
Meine Frage ist, ob jemand ne Ahnung hat wie man sowas realisiert
Irgendwie fehlen da noch Informationen. Ein funktionierendes Beispiel sähe so aus:
package Testmodul;
sub new {
# der Paketname
my $pkg = shift;
# die Codereferenz
my $ref = shift;
# Zuweisung
my $obj = {coderef => $ref};
# Rückgabe des Objektes
return bless($obj, $pkg);
}
1;
Und im Einsatz:
use strict;
use diagnostics;
use Testmodul;
my $instance = Testmodul->new( sub { print $_[0]; } );
$instance->{coderef}->('foo');
was der Unterschied ist in folgenden Zeilen:
$self_ = bless( %config, $self ); return $self_;
return bless( %config, $self );
Die erste Zeile speichert die Objektreferenz zwischen, um möglicherweise damit hinterher noch was zu machen (weitere Wertzuweisungen usw. usf.) und natürlich muss schlussendlich noch return $self_;
stehen. Die zweite Zeile gibt die Objektreferenz zurück, ohne sie zuvor zwischenzuspeichern.
Was bei dir allerdings genau schiefläuft, kann man wohl nur sagen, wenn du die dürftigen Infos durch entsprechende Codeteile aufbesserst und die gestellten Fragen beantwortest.
Siechfred
hier mal die 3 dateien, die auch funktionieren:
__DATEI__
package ModulA;
use strict;
use warnings;
use List::MoreUtils qw(uniq part);
sub new {
my ( $self ) = @_;
return bless( {}, $self );
}
{
my @Menu;
my $Menu_Funs;
sub get_Menu_Fun {
my ( $self ) = @_;
return $Menu_Funs;
}
sub add_Menu {
my ( $self, $code_ref, $menu ) = @_;
# create an array with splitted ([subitems], [items])
my @menu_items = part { !ref( $_ ) } @$menu;
# only the last part is of further interest
@menu_items = @{ $menu_items[1] };
# add the menu function for all items
@$Menu_Funs{ @menu_items } = ( $code_ref ) x scalar( @menu_items );
# bug or feature: mod_perl caches objects
# so we need to take care that items do not double
push @Menu, @$menu unless grep { /^$$menu[0]$/ } @Menu;
return @Menu;
}
}
1;
__DATEI__
package Modul1;
use strict;
use warnings;
sub new {
my ( $self, $ModulA ) = @_;
my %config;
$config{ ModulA } = $ModulA;
my $self = bless( %config, $self );
$ModulA->add_Menu( $self, [ "item1", "item2" ] );
return $self;
}
1;
__DATEI__
#!/usr/bin/perl
use 5.8.0;
use strict;
use warnings;
use CGI qw( :standard );
use Data::Dumper;
use lib "$ENV{ DOCUMENT_ROOT }/cgi-perl/modules/";
use ModulA;
use Modul1;
my $ModulA = ModulA->new();
my $Modul1 = Modul1->new( $ModulA );
print header();
print start_html();
print pre( Dumper( $ModulA->get_Menu_Fun() ));
print end_html();
1;
diese abgemagerte version tut was ich will. aber im gesamten projekt muss irgendwo noch was heimtückisches sein, werd ich wohl allein mühsam rausfinden müssen.
trotzdem danke.
alex
argh!
nach 6 stunden debugging hab ich den fehler gefunden.
in meinem $Web modul ist ein session objekt enthalten. das ist nicht weiter tragisch.
nur sollte man am ende des hauptprogrammes auch geöffnete sessions wieder schließen und somit das untie hash aufrufen.
och wie sowas nervt.
trotzdem danke an alle die hier versucht haben zu helfen
alex