Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris

Dereferencing %hash that does not exist.

by perldarren (Novice)
on Sep 04, 2013 at 10:09 UTC ( #1052277=perlquestion: print w/replies, xml ) Need Help??
perldarren has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I'm having some trouble and would appreciate some advice.

I have a couple of scripts that reference the same perl module.
In some of the scripts I want to pass a dispatch table to the module and in others I do not, where I do, I pass:
my %modifier = ( reverse_modifier => \&reverse_modifier ); my $return = my::module::exit_routine(\%modifier);
Where I don't I WANT to pass:
my $return = my::module::exit_routine();
In the module exit_routine dereferences %modifier like this:
my %revmod = %{$_[0]};
The scripts that do not pass %modifier result in:
Can't use an undefined value as a HASH reference at /usr/local/lib/perl5/site_perl/5.10.1/...

Whilst those that do pass %modifier work as expected.

My question is - How can I make this work for both scenarios? Preferably I would like to modify the module so that it can deal with the 2 scenarios and save me from modifying all of the scripts that use it.

Many thanks for your time and patience.


Replies are listed 'Best First'.
Re: Dereferencing %hash that does not exist. (conditional)
by Anonymous Monk on Sep 04, 2013 at 10:13 UTC
    :D perlintro#if
    my( $ref ) = @_; my %revmod; if( ref $ref ){ %revmod = %$ref; } ...

    Or  my %revmod = eval { %{ $_[0] } };

      Cool problem solved, thanks very much

      Never fails to amaze me how I sometimes just seem to have no grasp on something so fundamental :(

      I keep on trying though !

        Ok, so I implemented the eval variant of this and it seems to work well, thank you guys.
        I now have a slightly different issue.

        my::module::exit_routine is also passed into a simple logging module that I put together like this:
        my $log = LogSimple->new(logdir => $logdir, logfile => $logfile, exit +=> \&my::module::exit_routine )
        In this particular script whenever my::module::exit_routine is called I need to pass the %modifier dispatch table so I need to setup the $log object like this:
        my $log = LogSimple->new(logdir => $logdir, logfile => $logfile, exit +=> \&my::module::exit_routine(\%modifier) )
        Except this does not work, the error is:
        Not a CODE reference at /usr/local/lib/perl5/site_perl/5.10.1/

        I have to admit that my OOP is really in it's infancy.

        Really not sure on this one so any help much appreciated.

        Cheers, Darren
Re: Dereferencing %hash that does not exist.
by choroba (Chancellor) on Sep 04, 2013 at 10:28 UTC
    In recent Perl (which 5.10.1 is in this context), you can also use the defined-or operator:
    my $revmod = %{ $_[0] // {} };

    Update: It should work with plain ||, too, in the older versions. Just make sure you never call it with 0 as the argument.

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Dereferencing %hash that does not exist.
by perldarren (Novice) on Sep 04, 2013 at 20:31 UTC
    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/.../ 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.

      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{''} } 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;

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1052277]
Approved by Corion
[GrandFather]: discovering that one of the sensors for the device lies about the gain range it is using!
[GrandFather]: However, by the end of the day I had discovered its deceptions and now have it working correctly, so yes, all's well that ends well. :-D
[Corion]: GrandFather: Ah, (hardware) APIs - I have a similar situation with Chrome and its API... It is fairly underdocumented and I guess I have to hunt...
[Corion]: ... down supposedly working code to find out what I'm supposed to do
[GrandFather]: I haven't any "working code" to inspect! I have to find ways to generate reference signals then check the numbers I get at the far end match.
[GrandFather]: That's ok when the signal is a voltage, but there are three axis accelerometers, gyroscopes and magnetometers in these things! A little invention is needed at times!
[Corion]: GrandFather: Yeah, in that aspect, hardware is far more a black box than software
[GrandFather]: Oh, and the manufacturer tells lies too! I'm sure they don't mean to, but I know for sure some of the stuff they say is just wrong. It's possible they don't understand what we are asking, but that's not a good look either
[Corion]: GrandFather: Ah, unit testing hardware is fun, especially when the hardware is uncalibrated ...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2017-08-24 07:08 GMT
Find Nodes?
    Voting Booth?
    Who is your favorite scientist and why?

    Results (365 votes). Check out past polls.