Perl : Two packages in same file cannot import same package?

筅森魡賤 提交于 2019-12-03 20:33:31

In Perl, use Module is equivalent to

BEGIN { require Module; Module->import; }

But require caches the list of modules that have been required. It only loads the module once per Perl process. So only the first use IMPORTS does anything. Since your IMPORTS module doesn't have an import method, nothing happens when you use it again.

I'm not quite sure what you're attempting to accomplish. Perhaps your IMPORTS module should be an actual package, with an import method that exports whatever functions you want. That way, each use IMPORTS would export functions into the package that called it.

Joel Berger

use MyPackage is equivalent to BEGIN{ require MyPackage; MyPackage->import }. Inheriting from Exporter sets up an import class method which does the function "aliasing".

The problem is that you INCLUDES modules does not re-export the modules correctly. This is important because this is the process that imports the functions into the caller namespaces. While this isn't hard to craft on your own, there is a handy module for this purpose Import::Into.

Here is an example contained within a single file, it should be easy enough to reinflate into multiple, the only important difference is in the Includes module. I have made some other superficial changes but those are more for my taste.

#!/usr/bin/env perl

use strict;
use warnings;

package PACKAGE1;

use parent 'Exporter';
our @EXPORT = qw(Method1);

sub Method1 {
  print "PACKAGE1_Method1 \n";
}

package PACKAGE2;

use parent 'Exporter';
our @EXPORT = qw(Method1);

sub Method1 {
  print "PACKAGE2_Method1 \n";
}

package Includes;

use Import::Into;

# uncomment in mulitple files
#use PACKAGE1 ();  # prevent import
#use PACKAGE2 ();  # ditto

sub import {
  my $class = shift;
  my $caller = caller;

  PACKAGE1->import::into( $caller );
  PACKAGE2->import::into( $caller );
}

package Test1;
Includes->import; # in seperate files replace with `use Includes;`

Method1();

package Test2;
Includes->import; # ditto

Method1();

A real world example is the module utf8::all which makes extensive use of this mechanism to load lots of unicode stuff into the caller package.

Edit

To allow importing specific things from the Includes module, you could have it inherit from Exporter as well and craft its @EXPORT and @EXPORT_OK to do what you mean. Otherwise, you could continue with Import::Into and make something like bundles.

sub import {
  my $class  = shift;
  my $bundle = shift;

  my $caller = caller;

  if ($bundle eq 'Bundle1') {
    PACKAGE1->import::into( $caller );
    ... # other things in Bundle1
  } elsif ($bundle eq 'Bundle2') {
    PACKAGE2->import::into( $caller );
    ... # other things in Bundle2
  }
}

Then in your test modules

use Includes 'Bundle1';

In short crafting your own import method is not that hard, and every little is magical about Exporter. Once you learn about symbol table manipulation your don't need it or Import::Into, though it is a slightly more advanced topic. Here is a question I asked about it much earlier in my Perl days: Demystifying the Perl glob (*)

All that said, if object-oriented concepts of inheritance and polymorphism will do the job, you might want to investigate that route too. Here is an example of that:

#!/usr/bin/env perl

use strict;
use warnings;

package PACKAGE1;

sub Method1 {
  my $class = shift;
  print "PACKAGE1_Method1 \n";
}

sub Method2 {
  my $class = shift;
  print "PACKAGE1_Method2 \n";
}

package PACKAGE2;

# if multiple files use this
#use parent 'PACKAGE1';
# rather than
our @ISA = 'PACKAGE1';

# any methods in PACKAGE2 will override those declared in PACKAGE1 

sub Method1 {
  my $class = shift;
  print "PACKAGE2_Method1 \n";
}

package Test1;

# in seperate files need to use
#use PACKAGE2;

PACKAGE2->Method1();
PACKAGE2->Method2();

package Test2;

# ditto
#use PACKAGE1
#use PACKAGE2

PACKAGE2->Method1();
PACKAGE2->Method2();

# you can still use PACKAGE1 and get the originals
PACKAGE1->Method1();
PACKAGE1->Method2();

See now there is no Includes package and no symbols are imported into the Test* namespaces. PACKAGE2 provides Method2 because it inherits from PACKAGE1 and it does not override the method declaration with one of its own.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!