Link to home
Start Free TrialLog in
Avatar of holli
holli

asked on

call a modules INIT

This is a spinoff of thread:

The Question:

how can i call a modules INIT-section explicitly from another module or main or eval()?

example:

eval
qq{
      use Win32::API;
      Win32::API->Import( "winmm.dll", "long timeGetTime()" );
};

throws a warning because Win32::API::INIT is not executed within an eval-statement.

so how can i make INIT run without having to change the Win32::API´s code?



Avatar of holli
holli

ASKER

ASKER CERTIFIED SOLUTION
Avatar of terageek
terageek

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Change

if ($line =~ /INIT/) {

to

# Parse out comments
$line =~ s/(^|[^\$])#.*/$1/;
if ($line =~ /\bINIT\b/) {


That should skip cases where "INIT" is in a comment, and it will avoid issues with other words containing "INIT", like "$INITIALVALUE".
Avatar of holli

ASKER

i did a little investigation.

package t;

sub INIT
{
     print "INIT";
}

sub import
{
   my ($pkg, @args) = @_;
   print "package $pkg loaded, args ", join(':',@args), "\n";
   @foo::args = @args;
 
   my $init = $t::{INIT};
   my $foo  = $t::{foo};
   $init = \&$init;
   $foo = \&$foo;
   print "* $init *";
   print "* $foo *";
   &foo();
   &$init();
   print "end";
}

sub foo
{
     print "FOO";    
}

1;

______________


#t.pl
use lib qw (c:/ );
use t qw (x y);
print "main";


outcome:
package t loaded, args x:y
Undefined subroutine &t::INIT called at c://t.pm line 21.
BEGIN failed--compilation aborted at c:\t.pl line 2.
* CODE(0x186a230) ** CODE(0x186a170) *FOO


Why?
"CODE(0x186a230)" looks like a code-reference. why can´t i call it?
the main problem with this specific itin, it seems, is that id reads from the <DATA> stream. before you eval the code,(1) read until __DATA__, and (2) replace <DATA> with <PM>, or whatever you want to call the file handle. another few bugs needed fixing... use q(), not qq{} (both curley brackets and qq caused problems). there was a spot in the INIT that used # not in comment context... had to fix that

also, the init in this case is in API/type.pm not in API.pm

result (based on code above)
      foreach $path (@INC) {
    if (-e "$path/Win32/API/type.pm") {
      # Open the .pm file and grab the INIT block
        my $init_block = "";
        open PM, "<$path/Win32/API/type.pm";
        while ($line = <PM>) {
           #Start grabbing the INIT block
               if ($line !~ /\/[^\/]*#[^\/]*\//) {
               $line =~ s/(^|[^\$])#.*/$1/;}
           if ($line =~ /\bINIT\b/) {
                $init_block = $line;
            } elsif ($init_block ne "") {
                $init_block .= $line;
            }
            if ($init_block =~ /{/) {
               #Count the number of "{" and "}"
                my $open = scalar @{[$init_block =~ /\{/g]};
                my $closed = scalar @{[$init_block =~ /\}/g]};
               #Eval the block if the block is closed
                if ($open == $closed) {
                  # Get rid of "INIT" and make sure this
                  # is evaluated in the right package context
                    $init_block =~ s/^.*?INIT/package Win32::API::Type;/;
                     #set correct file handle
               *Win32::API::Type::DATA=*main::PM;
                      #read until _DATA_
                                $readline=<PM>;
                      while($readline !~ /__DATA__\n/){$readline=<PM>;};
                       ######back to old code#####
                    eval $init_block;
                              print $@."\n";
                  # Might need to set package back here???
                    $init_block = "";
                              last;
                }
            }
        }
    }
}


hope that helps
Avatar of holli

ASKER

that is ugly.

what do think about this? change:

INIT { #old code

to

INIT { &SINIT; }

sub SINIT { #old code

then you can call Module::SINIT();

quite elegant, isn´t it. or are there hidden penalties.
i am currently working on a module that automates that step by using a code-filter (Filter::Simple).
Avatar of holli

ASKER

i have this now:

Module Eval::INIT:

package Eval::INIT;

my $filter;

use Filter::Simple sub
{
      return if $filter++;
      
      my $i=0;
      while ( $_ =~ s/\bINIT(?!\x00)([\s\n]+)/\1INIT\x00 { \&SINIT$i; } \nsub SINIT$i\1/ms ) {$i++;};       
      s/\x00//msg;
};

1;

Module t.pm

package t;

use Eval::INIT;

INIT
{
      print "INIT\n";
}

1;

and t.pl

eval "use t; &t::SINIT0();";
print "main";

output:
INIT
main

what do you think?

beutiful! and it works for your problem... you do have to change the module code, but only by adding one line...

well, it would seem you solved you own problem!
Avatar of holli

ASKER

it would be nice if coud "inject" the use Evaled::Filter into the module, like

eval "use Evaled::InjectFilter qw (Win32::API::Type)";

from main::.

do you think this could make it to cpan?
what to do with the points?
the inject may be a problem... the INIT code has to run in Type.pm (cause of the <DATA>) so i'm not shure...
to put it on CPAN you will have to properly package it and stuff. you can try, if you like...
as for the points... terageek wrote a very tough piece of code, he deserves points.