Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

UPDATED, mostly solved: separation of define + assignment different from combination?

by perl-diddler (Hermit)
on Feb 11, 2014 at 18:50 UTC ( #1074468=perlquestion: print w/ replies, xml ) Need Help??
perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

This is no 'deep' subject or problem (that I know of), but it sorta surprised me.

If I have
my ($a, $b ) = (sub{}, sub{});
I would normally think that 'safe'.

I wanted to use '*', to call it as a named sub, so I had

(#1) local (*IO_glob, *NIO_glob) = (#2) (sub(){'IO'>'}, sub(){'<NIO>'});
Have used above code in a library for years w/no complaint. Then some program detected invalid args and was printing args that caused the prob. The above line generated a warning:
sub IO_GLOB redefined at line 2. sub NIO_GLOB redefined at line 2.

I was puzzled -- I thought the local would mean that each time through the code, I'd get a new instance of each var.

I figured that somehow, for some reason, the old versions of &IO_GLOB and &NIO_GLOB must stay around after the declaration and not be deleted until the end of the statement -- thus when they were replaced, I got the spurious message (which I can't figure out why it would be a problem or why it would tell me ... but, wanting just to fix the problem, I split the define and assignment:

(#1) local (*IO_glob, *NIO_glob); (#2) (*IO_glob, *NIO_glob) = (#3) (sub(){'IO'>'}, sub(){'<NIO>'});
Which got rid of the problem.

Thing is, I've used the 1st construct many times in creating sub's local to another sub (usually so they can share state & vars). This is the first time I've seen a redefinition error (perl5.14.3). Was it a fluke, or is there some repeatable way that the above could happen (other than running my original program, which did repeat it but it's far from short).

---- UPDATE ----

It seems it can happen if the subroutine that the 'local' is in is called recursively. I wondered about that -- but didn't think the case where it happened could have triggered a recursive call. It's possible something was so messed up in the calling program that some memory that would allow or disallow recursion at that point got stomped on... at least that's my best guess.

It DOES definitely happen w/recursion though... so that explains how it could happen... just not sure why it happened where it did .. but that's another issue (as that prog is still far from working! ;-)).

Just thought I'd update this w/latest findings.... Sorry for the bother...

At least now I know to split defines & assignments where they could be called recursively -- whereas didn't really know that before...

Comment on UPDATED, mostly solved: separation of define + assignment different from combination?
Select or Download Code
Re: UPDATED, mostly solved: separation of define + assignment different from combination?
by andal (Friar) on Feb 12, 2014 at 08:48 UTC

    Uph. It took me a while to understand what you are trying to say :)

    I guess, this might be considered a bug. Here's the example of code to reproduce it.

    use warnings; sub FUNC{ print "I'm in func\n"; } { local(*FUNC) = sub { print "now overridden\n"; }; FUNC(); } FUNC();
    The code above works correctly, but still produces warning. Somehow, warnings generator does not see that "local" shall save away old function reference and no redefining happens because after "local" function FUNC "does not exist" anymore.

      There is no bug, everything is working as it should, warnings is doing its job
      use warnings; sub FUNC{ print "I'm in func\n"; } { local(*FUNC) = sub { print "now overridden\n"; }; FUNC(); ## THIS IS LINE 6 } FUNC(); __END__ Subroutine main::FUNC redefined at - line 6. now overridden I'm in func
        Especially the comment "THIS IS LINE 6" on line 7 is nice.
        لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        The above doesn't really reproduce the error as 'FUNC' was in a 'sub' that got called recursively (which triggered the warning).

        Also, in that example, it is hard to see when the warning was 'issued', as STDERR is not buffered while STDOUT is, so the warning(on STDERR) would be seen (in this case), before any output from STDOUT (flushed @ program exit).

        I've rewritten the code below multiple times to experiment w/outcome. This reproduces the problem, and shows when an object containing the 'local' gets destroyed. There are two counters printed -- a "singleton" counter (1 for package), and a local copy of the global when the object was created (cuz they aren't the same on destruction). Hope this doesn't give anyone a headache like it did me in writing it...;-) Minor change w/change-line to allow breaking define+assign (i.e. via the '#' in column 2 (before the code) -- removing or not gets non-split vs. split define+assign

        #!/usr/bin/perl use warnings; use strict; use P; { package pkg; use warnings; use strict; use P; use Types::Core; our $cnt=0; sub new { my $p = shift; my $c = ref $p || $p; Pe "pkg %s created", ++$cnt; $p = bless { cnt => $cnt, fnc => sub { my $p2 = ref $_[0] ? shift : __PACKAGE__; Pe "fnc %d calling %s", $cnt, $p2->FUNC; undef; } }, $c; } sub destroy { Pe "pkg %s(%s) destroyed", $cnt, EhV $_[0], cnt; undef + } sub DESTROY { goto &destroy } } package main; sub callfunc(;$$); #silence warn about proto not ready sub callfunc (;$$) { my $p = pkg->new; my ($callfunc, $recur) = @_; $callfunc||=0; $recur||=0; local * FUNC #; *FUNC # change line = sub () { Pe "In FUNC, cnt=%s", $p->{cnt}; undef }; FUNC() if $callfunc; callfunc($callfunc, $recur) if $recur && $recur--; } Pe "** no recursion"; callfunc; callfunc 1; Pe "** split define/assign recursion"; callfunc 0,1; callfunc 1,1; pkg::destroy
        ---output for non-split:---
        ** no recursion pkg 1 created pkg 1(1) destroyed pkg 2 created In FUNC, cnt=2 pkg 2(2) destroyed ** split define/assign recursion pkg 3 created pkg 4 created Subroutine main::FUNC redefined at /tmp/tst2.pl line 29. pkg 4(4) destroyed pkg 4(3) destroyed pkg 5 created In FUNC, cnt=5 pkg 6 created Subroutine main::FUNC redefined at /tmp/tst2.pl line 29. In FUNC, cnt=6 pkg 6(6) destroyed pkg 6(5) destroyed pkg 6(&#8708;) destroyed
        ---output for split case ---
        ** no recursion pkg 1 created pkg 1(1) destroyed pkg 2 created In FUNC, cnt=2 pkg 2(2) destroyed ** split define/assign recursion pkg 3 created pkg 4 created pkg 4(4) destroyed pkg 4(3) destroyed pkg 5 created In FUNC, cnt=5 pkg 6 created In FUNC, cnt=6 pkg 6(6) destroyed pkg 6(5) destroyed pkg 6(&#8708;) destroyed
        I used Types::Core's EhV, to allow dereferencing $_[0] and returning the value of $_[0] in 1 step. I used P's 'Pe' instead of 'P' to print to STDERR so my output would be unbuffered and intermixed with perl's STDERR stream.

        Side note: In case your local font doesn't display the default 'undef' sign used by P, (present in parens on the last line of output), it looks like 'E' with a diagonal crossout through it, but backward (in case your font doesn't display it).

        As the output shows above, the warning message only occurs on recursive calls to the sub where the local defines the sub (and breaking the local into a separate declaration followed by the assignment doesn't show the problem....ah crud.. didn't put that in the example -- **UPDATED**

        Now we can see that the recursive value gets destroyed before returning so no overwrite is detected...

        p.s. I note the <code> function is broken -- it doesn't post output "as is", but turns it into non-standard, decimal HTML (i.e. &&;#8708 is a decimal number, and current standards default to hex as in "&&;#x2204;" ).

        p.p.s. -- I note that while the <code> doesn't maintain literal values in displaying code, these comments do, so if your charset has the character it should be between the quotes: "∄". Cheers!

        There is no bug, everything is working as it should, warnings is doing its job

        Well, first of all you made mistake, the line 6 is where the assignment is done. Second, if you separate the assignment from localizing, then no warning is produced. According to documentation, there should be no difference. Whether one writes

        local(*FUNC) = sub {};
        or
        local(*FUNC); *FUNC = sub {};
        things should be the same. But they are not. First one produces warning, second one - not. That is why I said, that it might be considered a bug.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1074468]
Approved by Laurent_R
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2014-09-24 01:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (244 votes), past polls