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

Dear Monks:

This is once more a question to a problem which I have already solved in a different way, but where I would like to know why my first attempt failed.

Inspired by a suggestion I got for a different problem, I now wanted to solve the following task: Write a module Master.pm which, when called like this:

package Slave; use Master;
would cause a function nx defined inside the package of Slave. My first attempt went like this:
package Master; use strict; use warnings; sub import { my $target = caller; no strict 'refs'; *{"$target\::nx"} = sub { do_something() } }
Using the debugger, I indeed could see that this function would end up in the symbol table of package Slave; but when I defined do_something as:
package Slave; use strict; use warnings; use Master; sub do_something { my $pkg=caller(); print "$pkg\n"; }
I found that this prints Master, not Slave as the calling package. This puzzled me. Well, I ended up writing the defining line in the import function like this:
eval "package $caller; sub nx { do_something() }";
and this works now as expected, but I still would like to know why my original attempt had failed.

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: Defining a function in the caller's package
by betterworld (Curate) on Aug 08, 2008 at 11:29 UTC

    Hi, maybe chromatic's response in the thread How to fool caller() / use NEXT within a dynamic sub might help you. (Update: Hmm, I'm not sure if this works here, because the problem in that thread was the name of the sub, not the package.)

    caller always returns the package where the code was, regardless of which name in which package you used to call the code.

    By the way, what you are trying to do (creating a sub in the importing package) is basically what Exporter does, however it aliases it to a sub in the imported package.

      caller always returns the package where the code was, regardless of which name in which package you used to call the code.

      I see, this explains why my first attempt had to fail.

      Actually, the reference you gave was very helpful, because what my do_something *really* does, is to call NEXT(). I just left out this detail because I thought it would not be relevant to the problem.

      I just wonder whether fiddling around with __ANON__, as shown in the example you refered to, is considered portable Perl style (I couldn't find any "official" Perl documentation about __ANON__), or whether the eval solution I used would be better. Both seem to be a hack, but which one is the kinder hack ?-)

      -- 
      Ronald Fischer <ynnor@mm.st>

        t/op/caller.t and t/comp/package.t in the core test suite both test aspects of __ANON__'s behavior. I consider that official enough to believe that it won't go away.

Re: Defining a function in the caller's package
by jettero (Monsignor) on Aug 08, 2008 at 11:50 UTC
    You can change the package locally...

    *{"$target\::nx"} = do { package soemthing; sub { do_something() } } ... The problem seems to be that your anonymous sub remembers where it was first compiled.

    I'd recommend doing this job with Exporter though. Is there some reason that doesn't fit? (Probably because it would use the master namespace, now that I think about it.)

    -Paul

      Is there some reason that doesn't fit? (Probably because it would use the master namespace, now that I think about it.)

      Exactly for this reason. BTW, I like your suggestion using do. Clearer than my eval and, maybe, also clearer than messing around with __ANON__.

      -- 
      Ronald Fischer <ynnor@mm.st>

      On second thought, your solution does not work for me, sadly. The problem is that I don't know the package name beforehand. Hence I would have to write something like:

      .... do { package $target; ... }
      but, as I tried out right now, you can't pass variables to the package statement. Hence I'll have to stick with eval.

      -- 
      Ronald Fischer <ynnor@mm.st>
        Yeah, I noticed that. package should take expression arguments imo... You're pretty much forced to use eval because of that. I imagine there's some trick to set the package using symbol table magic, but it's probably not recommended.

        -Paul

Re: Defining a function in the caller's package
by mr_mischief (Monsignor) on Aug 08, 2008 at 16:13 UTC
    Don't make a closure. Just assign the typeglob.
    package Foo; sub do_something { print scalar caller, "\n"; } sub import { my $target = caller; no strict 'refs'; *{"$target:\:do_something"} = \&do_something; } 1;
      Don't make a closure. Just assign the typeglob.

      This would not solve my problem (sorry if I was not precise enough about this). If I would adapt your example for my application, it would look more like:

      package ForeignModule; sub NEXT { print scalar caller } ================================= package Foo; use ForeignModule qw(NEXT); sub do_something { NEXT(); } sub import { my $target = caller; no strict 'refs'; *{"$target:\:do_something"} = \&do_something; } ================================= package main; use Foo; do_something();
      This would print 'Foo', but I would like to have it printed 'main'. This can be accomplished using, for instance, the eval "package ...." mechanism.

      -- 
      Ronald Fischer <ynnor@mm.st>
        Don't do that. You're assigning a sub in Foo to return the results of a call made to ForeignModule. Then you're setting main::do_something to be the same as that subroutine. What you actually want is not to return the value but to make main::do_something the very same sub as ForeignModule::NEXT.
        package ForeignModule; sub NEXT { print scalar caller } 1;
        package Foo; # there is no do_something sub import { my $target = caller; no strict 'refs'; *{"$target:\:do_something"} = \&ForeignModule::NEXT; } 1;
        package main; use strict; use warnings; use Foo; do_something();
Re: Defining a function in the caller's package
by Bloodnok (Vicar) on Aug 08, 2008 at 11:40 UTC
    Hi ,

    I'm no expert on this sort of thing but ...

    Opens mouth and prepares to insert foot ...

    I think that the initial problem was/is down to the context of the anonymous sub declaration - the Master package - as reported by the initial run.

    Your subsequent use of eval changed the context to that of the useing package i.e. Slave, as required and thus you solved your problem - as you pointed out.

    There now, I think I've put my foot in it enough ... ;-)

    A user level that continues to overstate my experience :-))