Any way to "inject" an INIT block into a different module?

Abigail abigail at abigail.be
Fri May 30 15:43:49 BST 2008


On Fri, May 30, 2008 at 01:26:36PM +0100, Andy Wardley wrote:
> I'm messing with INIT blocks, trying to get some methods auto-generated at
> the right time.
> 
> Here's an example.  Let's say I have an amplifier.  Naturally, it goes up
> to eleven.
> 
>     package Amp;
>     use Control qw( volume sustain );
> 
>     sub volume {
>         print "This amp goes up to eleven\n"
>     }
> 
> My Control module looks something like this:
> 
>     package Control;
> 
>     our @HOOKS;
>     INIT { export(@_) for @HOOKS; }
> 
>     sub import {
>         my $class = shift;
>         push(@HOOKS, [(caller(0))[0], @_]);
>     }
> 
>     sub export {
>         my $target = shift;
>         no strict 'refs';
> 
>         foreach my $sub (@_) {
>             if (defined &{"${target}::$sub"}) {
>                 print "* skipping ${target}::$sub (already defined)\n";
>             }
>             else {
>                 print "* creating ${target}::$sub\n";
>                 *{"${target}::$sub"} = sub {
>                     print "Control $sub\n";
>                 };
>             }
>         }
>     }
> 
> It creates simple accessor methods in the Amp class, or indeed any class
> that you C<use> it in.  In this case, they're just dummy subs, but you
> get the idea.
> 
> Because I don't want these auto-generated methods to overwrite any existing
> methods that I've written (like volume()), we skip over any methods that
> are already defined.  But to do this, we have to wait until the INIT phase
> to give Perl a chance to parse all the source code and figure out what
> methods have or haven't been defined.  So the import() method pushes the
> stuff onto @HOOKS for the INIT block to subsequently pipe back via export().
> 
> So far so good (or evil, depending on your proximity to the crack pipe).
> This small test program:
> 
>     use Amp;
>     Amp->volume;
>     Amp->sustain;
> 
> Generates this output:
> 
>     * skipping Amp::volume (already defined)
>     * creating Amp::sustain
>     This amp goes up to eleven
>     Control sustain
> 
> At first glance this is most satisfactory.  If I'm on already on ten, all
> the way up, on ten on my guitar, and I need that extra push over the cliff,
> I can go up to eleven.  And I could go away and have a cup of tea and still
> be hearing that sustain.
> 
> However, behind the glitzy facade of the topsy-turvy world of rock'n'roll,
> there limps a small and fragile pony over-burdened with a heavy load of 
> FAIL.
> If you try and load the Amp module dynamically (let's say we're starting a
> band and we haven't yet decided if we're going to be electric or acoustic),
> like so:
> 
>     use Control qw( sex drugs );
> 
>     # later...
>     eval "use Amp";
>     Amp->volume;
>     Amp->sustain;
> 
> Then it blows chunks thusly:
> 
>     This amp goes up to eleven
>     Can't locate object method "sustain" via package "Amp" at ...blah
> 
> Because, of course, the INIT time for my Control class has come and gone.
> 
> Now I realise that I can work around this by forcing the caller to do some
> extra work, e.g. define their own INIT block or call a specific method
> to generate the methods at the right time.  But I'm trying to figure out
> if there's a way around it without requiring any extra work in the caller's
> package.  If only for chuckles.
> 
> What I think I want to do, in conceptual terms at least, is to have my
> Control module "export" an INIT block/action into the Amp module.  I know
> I can't do that (at least I don't think I can, can I?) but that's the effect
> that I'm after.


No, you cannot, unless you can solve the halting problem. In your example:

    use Control qw( sex drugs );

    eval "use Amp";
    Amp->volume;
    Amp->sustain;


the biggest problem isn't so much as to "inject" something, but by the
time Control gets compiled/called, there's no way to know in which 
package it should add subs, as the eval "use Amp" happens later.

> Any ideas?


Well, if it's acceptable to do the "use Control" from the Amp.pm module
(as you were doing it initially), wouldn't moving the "use Control" to
end of the package solve the problem? By the time Control -> import ()
gets called, all the subs of Amp.pm are defined.



Abigail
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://london.pm.org/pipermail/london.pm/attachments/20080530/281d043a/attachment-0001.pgp


More information about the london.pm mailing list