Solved

call a modules INIT

Posted on 2004-08-25
10
254 Views
Last Modified: 2010-03-05
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?



0
Comment
Question by:holli
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 3
  • 2
10 Comments
 
LVL 6

Author Comment

by:holli
ID: 11891917
0
 
LVL 3

Accepted Solution

by:
terageek earned 500 total points
ID: 11896158
I don't know if this will work, and it isn't too direct or pretty, but here is a shot...

#Search @INC for Win32::API
foreach my $path (@INC) {
    if (-e $path/Win32/API.pm) {
      # Open the .pm file and grab the INIT block
        my $init_block = "";
        open PM "<$path/Win32/API.pm");
        while ($line = <PM>) {
           #Start grabbing the INIT block
            if ($line =~ /INIT/) {
                $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;/;
                    eval $init_block;
                  # Might need to set package back here???
                    $init_block = "";
                }
            }
        }
        last;
    }
}

The above code makes some assumptions about proper formatting of the pm code.  You may also need to put yourself back into your own package.
0
 
LVL 3

Expert Comment

by:terageek
ID: 11896351
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".
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 6

Author Comment

by:holli
ID: 11900641
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?
0
 
LVL 1

Expert Comment

by:boazgOLD
ID: 11901109
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
0
 
LVL 6

Author Comment

by:holli
ID: 11902791
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).
0
 
LVL 6

Author Comment

by:holli
ID: 11903148
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?

0
 
LVL 1

Expert Comment

by:boazgOLD
ID: 11904634
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!
0
 
LVL 6

Author Comment

by:holli
ID: 11905023
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?
0
 
LVL 1

Expert Comment

by:boazgOLD
ID: 11905171
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.
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I have been pestered over the years to produce and distribute regular data extracts, and often the request have explicitly requested the data be emailed as an Excel attachement; specifically Excel, as it appears: CSV files confuse (no Red or Green h…
A year or so back I was asked to have a play with MongoDB; within half an hour I had downloaded (http://www.mongodb.org/downloads),  installed and started the daemon, and had a console window open. After an hour or two of playing at the command …
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…

751 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question