in reply to Dereferencing %hash that does not exist.

Thought I ought to make this thread a little clearer - I thought my problem was gone but it seems that it's only just started:
From my script I can do these ok:
my $return = my::module::exit_routine(); sub reverse_modifier { # I modify default behaviour of the exit routine if I get passed - som +e times I am not needed. } my %modifier = ( reverse_modifier => \&reverse_modifier ); my $return = my::module::exit_routine(\%modifier);
In my::module::exit_routine I can deal with %modifier being present or not:
my %revmod = eval { %{ $_[0] } }
But here is my new sticking point - I am unable to find a way to do this from my script:
my $log = LogSimple->new(_exit => my::module::exit_routine(\%modifier) + ); $log->err("An error occured, _exit will happen before I exit but now I + want to force exit_routine to also use the \%modifier as well")
When an error is trapped and $log->err is executed I get:
Not a CODE reference at /usr/local/lib/.../LogSimple.pm line 122:
From my LogSimple package line 122 = $exit_ref->($self);


Effectively I am trying to send a reference which has another reference as one of it's parameters and my syntax is completely jiggered.
Infact, the whole concept could be busted up now that I am trying to introduce %modifier to plug up a hole I never knew I had.
Package LogSimple ... sub wlog { my ($self,$MESSAGE,$ERR,$LEVEL,$LOGLEVEL) = @_; my $TIME = datetime(); my $exit_ref; if ( $LOGLEVEL <= $self->{_loglevel} ) { open (LOG, ">>$self->{_log}") || warn "Unable to open log file + $self->{_log}: $!"; print LOG "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n"; print "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n" if ( $se +lf->{_verbosity} > 0 ); close (LOG); } if ( $ERR ) { if ( exists $self->{_exit} ) { use Data::Dumper; print Dumper $self->{_exit}; $exit_ref = $self->{_exit}; $exit_ref->($self); # LINE 122 if ( $LOGLEVEL <= $self->{_loglevel} ) { open (LOG, ">>$self->{_log}") || warn "Unable to open log file + $self->{_log}: $!"; print LOG "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n"; print "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n" if ( $se +lf->{_verbosity} > 0 ); close (LOG); }; } exit $ERR } }
Any assistance much appreciated.

Regards,
Darren

Replies are listed 'Best First'.
Re^2: Dereferencing %hash that does not exist.
by ig (Vicar) on Sep 04, 2013 at 21:22 UTC

    The Dumper output from line 120 should make it clear what $exit_ref will be set to but the error message makes it clear that it is not a code reference. My guess is that my::module::exit_routine(\%modifier) doesn't return a code reference, though it is only a guess as much of your code is not presented.

      Dumper output $VAR1 = \'';
      If I remove %modifier and send exit => \&Hitachi::Raidcom::exit_routine there's a whole bunch of $VARS but if I try to add the argument as in this snippet, it breaks.

      Here is the reverse modifier sub and the dispatch table:

      sub reverse_modifier { my (@undo) = @{$_[0]}; my @rdis; my @rdel; my @newundo; for my $cmd (@undo) { if ( $cmd =~ /lock/ ) { push(@newundo, $cmd) } if ( $cmd =~ /disconnect/ ) { push (@rdis, $cmd) } if ( $cmd =~ /delete/ ) { push (@rdel, $cmd) } if ( $cmd =~ /unlock/ ) { push(@newundo,(@rdis,"sleep +22",@rdel,$cmd)) } } return @newundo; } my %modifier = ( reverse_modifier => \&reverse_modifier ); # Setup the log object my $log = LogSimple->new( logdir => $logdir, logfile => $logfile, logl +evel => $loglevel, verbosity => $verbosity, exit => \&Hitachi::Raidco +m::exit_routine(\%modifier) );
      This is the entire LogSimple module:
      package LogSimple; use strict; use Time::Format qw(%time %strftime %manip); use Exporter; use vars qw($VERSION @ISA @EXPORT); our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not expo +rt # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use LogSimple ':all'; # If you do not need this, moving things directly into @EXPORT or @EXP +ORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( ); our @EXPORT = qw( ); our $VERSION = '0.01'; # Preloaded methods go here. sub new { my $class = shift; my %options = @_; my $self = { _logdir => 'LogDEC_Dir', _logfile => 'logfile.log', _loglevel => '5', _verbosity => '1', _script => $0, }; if ( exists $options{'logdir'} ) { $options{'logdir'} =~ s/\///g; $self->{_logdir} = $options{'logdir'} } else { # user didn't specify logdir, tell them where the default was +created print "Default log directory: $self->{_logdir} - change it to +not see this message\n"; } if ( exists $options{'logfile'} ) { $self->{_logfile} = $options{' +logfile'} }; if ( exists $options{'loglevel'} ) { $self->{_loglevel} = $options +{'loglevel'} }; if ( exists $options{'verbosity'} ) { $self->{_verbosity} = $optio +ns{'verbosity'} }; if ( exists $options{'exit'} ) { $self->{_exit} = $options{'exit'} + }; $self->{_log} = "$self->{_logdir}/$self->{_logfile}"; unless ( -w $self->{_logdir} ) { mkdir ( $self->{_logdir}, 0700 ) || die "Unable to create log +directory $self->{_logdir}: $!"; }; bless $self, $class; return $self; } sub datetime { return $time{'dd-mm-yyyy_hh.mm.ss'} } sub inf { my ($self,$MESSAGE) = @_; my @PASS; push(@PASS,@_); push(@PASS,("","INF", 3)); wlog(@PASS); } sub wrn { my ($self,$MESSAGE) = @_; my @PASS; push(@PASS,@_); push(@PASS,("","WRN", 2)); wlog(@PASS); } sub dbg { my ($self,$MESSAGE) = @_; my @PASS; push(@PASS,@_); push(@PASS,("","DBG", 4)); wlog(@PASS); } sub raw { my ($self,$MESSAGE) = @_; my @PASS; push(@PASS,@_); push(@PASS,("","RAW", 5)); wlog(@PASS); } sub err { my ($self,$MESSAGE,$ERR) = @_; my @PASS; push(@PASS,@_); push(@PASS,("ERR", 1)); wlog(@PASS); } sub wlog { my ($self,$MESSAGE,$ERR,$LEVEL,$LOGLEVEL) = @_; my $TIME = datetime(); my $exit_ref; if ( $LOGLEVEL <= $self->{_loglevel} ) { open (LOG, ">>$self->{_log}") || warn "Unable to open log file + $self->{_log}: $!"; print LOG "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n"; print "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n" if ( $se +lf->{_verbosity} > 0 ); close (LOG); } if ( $ERR ) { if ( exists $self->{_exit} ) { use Data::Dumper; print Dumper $self->{_exit}; $exit_ref = $self->{_exit}; $exit_ref->($self); if ( $LOGLEVEL <= $self->{_loglevel} ) { open (LOG, ">>$self->{_log}") || warn "Unable to open log file + $self->{_log}: $!"; print LOG "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n"; print "$LEVEL: $TIME : $self->{_script} : $MESSAGE\n" if ( $se +lf->{_verbosity} > 0 ); close (LOG); }; } exit $ERR } } 1;

        OK, so Dumper output $VAR1 = \''; shows that $self->{_exit} is not a code ref, as expected. You haven't shown the code of Hitachi::Raidcom::exit_routine, but with the error message and Dumper output, it is reasonably clear what is happening.

        Your code samples seem to be alternating between exit => \&Hitachi::Raidcom::exit_routine(\%modifier) and _exit => my::module::exit_routine(\%modifier), among other alternatives.

        Hitachi::Raidcom::exit_routine(\%modifier) executes the subroutine and yields the value it returns.

        &Hitachi::Raidcom::exit_routine(\%modifier) does the same. The & is optional in this case (with the arguments in parentheses) and makes no difference.

        \&Hitachi::Raidcom::exit_routine(\%modifier) also runs the subroutine and yields a reference to the value it returns. That's why dumper gives you \''. The \ indicates a reference. In this case, a reference to the empty string.

        What will probably work for you is (as AnomalousMonk suggested):

        # Setup the log object my $log = LogSimple->new( logdir => $logdir, logfile => $logfile, logl +evel => $loglevel, verbosity => $verbosity, exit => sub { Hitachi::Ra +idcom::exit_routine(\%modifier) } );

        With this initialization, $self->{_exit} should be set to a code reference: to the anonymous sub which does nothing but call Hitachi::Raidcom::exit_routine with the argument \%modifier and return whatever that sub returns. The sub isn't executed immediately. It is executed later, in sub wlog, when the code reference is de-referenced and called. But, I haven't tested...

        While this will probably work for you, I will mention closure (just one of many articles on the subject). It is a confusing feature for many, so I suggest you not worry about it immediately, but once you have this working, learn about closure before making too much use of anonymous subroutines accessing lexical variables.