http://qs1969.pair.com?node_id=11128808

Bod has asked for the wisdom of the Perl Monks concerning the following question:

I have a bit of a strange problem... I have consulted the very useful assistance haukex and others provided on Re^9: Preparing data for Template.

The problem is dereferencing an entry in a hash. At first, I thought it was the trouble I always seem to run into when using references. So, I built some test code to check what I was doing and it works as expected.

use strict; my $val = 'A'; my $vars = { 'test' => $val }; reftest('testing', $vars); sub reftest { my ($value, %vars) = @_; print "$value\n"; print $$vars{'test'}."\n"; } C:\Users\ian\Perl>perl dereference.pl testing A
The output is exactly as expected.

But the code on the webserver is structured the same. The only difference is that the subroutine is a method in a blessed object...could this really make a difference?

The calling code...

my $vars = { 'testpage' => $data{'testpage'} }; $html->process('index', $vars);

And the method within the $html object...

sub process { my ($self, $disp, %v) = @_; my $testpage = $$v{'testpage'}; print "<h1>".$testpage."</h1>\n"; }
The output is <h1></h1> only.

I have checked the $data{'testpage'} contains the expected data of either 'A' or 'B'.
What is causing the lack of dereferencing or what else could I try to debug this problem?

Replies are listed 'Best First'.
Re: Dereferencing in blessed object
by LanX (Saint) on Feb 25, 2021 at 23:08 UTC
    > The output is exactly as expected.

    you should use warnings ... always!

    Reference found where even-sized list expected at - line 16.

    the only reason why your output seems to be correct is that you are dereferencing the $vars hash-ref from the closure° not the supposedly passed %vars .

    please note that %vars and $vars are two different variables in separate namespaces.

    try

    my ($value, $vars) = @_;

    and do yourself a favor and write

    $vars->{test}; instead of $$vars{test}

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    °) "global" might be easier to understand

    Update

    Stripped quotes from hash-key

      the only reason why your output seems to be correct is that you are dereferencing the $vars hash-ref from the closure

      Oh!
      Yes, changing the variable name in the subroutine 'breaks' it as expected...now I understand why the different bits of seemingly identical code behave differently! Thanks Rolf.

      $vars->{'test'}; instead of $$vars{test}

      I did have $vars->{'test'}; in there at one point whilst testing it.
      Why is this preferable to $$vars{test}?

        Both are legal syntax, the overwhelming mainstream tends to $vars->{'test'} probably since PBP.

        So many might have problems reading the other form.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

        Oh! Yes, changing the variable name in the subroutine 'breaks' it as expected
        To avoid wasting time chasing silly mistakes like these in future, I suggest you always -- as a matter of routine habit -- order your file with functions first and mainline at the end.

        An alternative is to leave the mainline first but put a bare block around it, to ensure mainline global variables don't accidentally leak into the subroutines defined below.

        Why is this preferable to $$vars{test}?

        I don't see objectively why it would be, TBH. If you start getting into deeper structures then the increasing levels of syntax needed to deal with the $$foo{bar} equivalents is going to get messy. For a simple hash though there's little difference.

        Personally I do use the arrow operator as it (subjectively) stands out more clearly to me in an eye-parse. But I do not quote hash keys (whether in references or otherwise) unless necessary. If I see code like $vars->{'test'} it makes me pause and wonder why the key is quoted.

        Whatever you decide to do, make a plan and stick to it. Having your own coding standard, at least within one project, will be a benefit. Inconsistency is the bug's friend.


        🦛

        $$vars{'test'} is a visual pain for me because of the split millisecond where I ask myself about precedence. OTH the -> in $vars->{'test'} dates back to at least C where is used in exactly the same way and context.

        Performance-wise I don't think there is a difference (OSX 10.13 and perl 5.28.3):

        use Benchmark qw( cmpthese ); cmpthese(-5, { deref => 'use strict; use warnings; my $x = { map { $_ => rand + } 1..50 }; $x->{$_} = $_ for 1..100', none => 'use strict; use warnings; my $x = { map { $_ => rand + } 1..50 }; $$x{$_} = $_ for 1..100' }); Rate deref none deref 12483/s -- -1% none 12611/s 1% -- Rate none deref none 11938/s -- -0% deref 11967/s 0% --

        Or,

        use Benchmark qw( cmpthese ); cmpthese(-5, { deref => 'use strict; use warnings; srand 42; my $x = { map { rand() => rand() } 1..50 }; srand 42; $x->{rand()} = rand() for 1..100', none => 'use strict; use warnings; srand 42; my $x = { map { rand() => rand() } 1..50 }; srand 42; $$x{rand()} = rand() for 1..100' });

        For benchmarks see also: https://stackoverflow.com/questions/18984323/how-expensive-is-it-to-dereference-an-array-ref-in-perl

        bw, bliako

      "you should use warnings ... always!"

      Despite being told and shown why this is a good idea, multiple times, it's still not sinking in.

        Understandably ... he's too busy telling us how to improve this site to improve his code... ;-)

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Dereferencing in blessed object
by haukex (Archbishop) on Feb 26, 2021 at 09:20 UTC
    What is causing the lack of dereferencing or what else could I try to debug this problem?

    The issue with my ($self, $disp, %v) = @_; and my ($value, %vars) = @_; is the same as Re^7: Preparing data for Template. Building an SSCCE to understand it better is a great approach, but since the point of it is also to debug, make sure you pull out all the stops: In this case, Use strict and warnings, use useful variable names, and don't repeat variable names. See also the Basic debugging checklist.

    Here's another way to think about the issue: @_ contains the subroutine arguments, so the first line of reftest can be thought of as my ($value, %vars) = ('testing', $vars);. Drop the $value and you get my %vars = $vars; - there's no dereferencing happening here. In effect, it's the same as my %vars = ( $vars => undef );, which you can see when you dump \%vars with e.g. Data::Dump: you'll see { "HASH(0x...)" => undef }, the hashref was stringified since hash keys are strings.

    Also, variable naming/scoping is biting you as well: $$v{'testpage'} aka $v->{testpage} is accessing a scalar $v, which is not defined in sub process, and so apparently you've got a $v somewhere higher up in the code that is either uninitialized and being autovivified here, or it's already a hashref, or, in the worst case, you're not using strict. And note that if $$v{'testpage'} is giving you undef, "<h1>".$testpage."</h1>\n" would have given you warnings accordingly.

    As before, you need to either explicitly dereference the hashref via my %hash = %$hashref;, although that makes a (shallow) copy of the hash, or you can save on memory and just use the hashref as-is, which is what you're trying to do in $$v{'testpage'}, but to do that, you should've written my ($self, $disp, $v) = @_; instead.

    Just for completeness:

    The only difference is that the subroutine is a method in a blessed object...could this really make a difference?

    No, method calls don't affect this.

      Thanks haukex - that's extremely helpful.

      or, in the worst case, you're not using strict.

      The main script has use strict; but it seems that does then apply to the module it calls...I know that now!

      So now I've added use strict; I get a whole bunch of Global symbol requires explicit package name errors even though they all have my or our declarations. I tracked this down to the declaration happening later in the module than methods that are throwing the error.

      It also highlighted another error with this:

      $db =~ s/_.+?_/_$dbname_/;
      The variable $dbname is declared but in the regexp it is looking for $dbname_. I have solved this for now with:
      my $temp = "_$dbname"."_"; $db =~ s/_.+?_/$temp/;
      But - I feel sure there is a more elegant way!

        Use braces:

        $db =~ s/_.+?_/_${dbname}_/;

        🦛

        The main script has use strict; but it seems that does then apply to the module it calls...I know that now!

        The scope of strict and many other pragmas like warnings is lexical: if you put it at the top of a file, its scope is only that file, and does not extend to any files included with do, require, or use.

        But - I feel sure there is a more elegant way!

        Though I concur with hippo that braces are easiest here, in the spirit of TIMTOWTDI note there's also the /e modifier: $db =~ s/_.+?_/"_$dbname"."_"/e;