4

2つのパッケージを使用して、一方から他方に関数を呼び出そうとしていますが、次のエラーが発生します。

未定義のサブルーチン&module2 ::method_1_2がmodule2.pmの20行目で呼び出されました。

このエラーが発生せずに、あるパッケージから別のパッケージに関数を呼び出す方法はありますか?

前もって感謝します。

シャビ

execエラー:

./test.pl
method_1_1
method_2_1
method_2_2
Undefined subroutine &module2::method_1_2 called at module2.pm line 20.

サンプルコード(test.pl):

#!/usr/bin/perl

use strict;
use module1;
use module2;

method_1_1();
method_2_2();

module1.pm

package module1;

use strict;

use module2;

require Exporter;
use vars qw(@ISA @EXPORT);
@ISA     = qw(Exporter);
@EXPORT  = qw( method_1_1 method_1_2 );

sub method_1_1
{
   print "method_1_1\n";
   method_2_1();
}

sub method_1_2
{
   print "method_1_2\n";
}

1;

module2.pm:

package module2;

use strict;

use module1;

require Exporter;
use vars qw(@ISA @EXPORT);
@ISA     = qw(Exporter);
@EXPORT  = qw( method_2_1 method_2_2 );

sub method_2_1
{
   print "method_2_1\n";
}

sub method_2_2
{
   print "method_2_2\n";
   method_1_2();
}

1;
4

3 に答える 3

3

@EXPORT問題は、すでに使用された後に割り当てることです。以下はMini-Tutorialからコピーされたものです:エクスポートモジュールの相互使用


[この手法を使用する必要性は、システムの設計上の欠陥を示す非常に強力な指標ですが、設計上の欠陥を修正するためのリソースが常に利用できるとは限らないことを認識しています。]

ModAがModBを使用し、ModBがModAを使用し、ModAまたはModBが他方からシンボルをインポートする場合、一方はコードの実行順序に注意を払う必要があります。問題を回避するために私が見つけた最善の方法は、他のモジュールをロードする前にエクスポーターをセットアップすることです。

# ModA.pm

package ModA;

use strict;
use warnings;

use Exporter qw( import );
BEGIN { our @EXPORT_OK = qw( ... ); }

use This;
use ModB;
use That;

...

1;

# ModB.pm

package ModB;

use strict;
use warnings;

use Exporter qw( import );
BEGIN { our @EXPORT_OK = qw( ... ); }

use This;
use ModA;
use That;

...

1;
于 2012-07-17T15:33:28.123 に答える
3

問題は、最初に行うことmodule1use module2. つまり、コンパイルmodule2中にすべてが読み取られ、実行されます。module1

次に起こることは、そうするということmodule2ですuse module1module1が見つかって Perl に入れられたため%INC、再度実行することはなくmodule1->import、エクスポートされたシンボルをフェッチするだけです。

しかしもちろんmodule1、実際にはほとんどコンパイルが開始されて@module1::EXPORTおらず、存在すらしていません。その 2 つのサブルーチンは気にしないでください。これにより、Exporterimport は にまったく何もインポートされないmodule2ため、呼び出しを行うとき、それmethod_1_2()について何も知りません。

これを修正する最もクリーンな方法は、コンパイル(すべてのuseステートメントとBEGINブロックを含む)で実行前にインポートを行うことです。Perl のINITブロックはこれに最適で、モジュールを以下の形式に変更することでコードを機能させることができます。module2呼び出しのパターンは、この特定の問題を修正するために必要なのはこれだけであることを意味するため、ここでのみ示しましたが、一般的なケースでは、すべての協力モジュールに同等の変更が必要です。

package module2;

use strict;
use warnings;

use module1;
INIT { module1->import }

use base 'Exporter';
our @EXPORT  = qw( method_2_1 method_2_2 );

sub method_2_1 {
   print "method_2_1\n";
}

sub method_2_2 {
   print "method_2_2\n";
   method_1_2();
}

1;
于 2012-07-17T13:36:48.527 に答える
1

興味深いことに、なぜ名前空間method_1_2にエクスポートされないのかわかりませんmodule2が、パッケージを明示的に参照することでこれを回避できます。

module1.pm

package module1;

use strict;
use warnings;

use module2 (); #don't import methods

use base 'Exporter';
our @EXPORT  = qw( method_1_1 method_1_2 );

sub method_1_1
{
   print "method_1_1\n";
   module2::method_2_1();
}

sub method_1_2
{
   print "method_1_2\n";
}

1;

module2.pm

package module2;

use strict;
use warnings;

use module1 (); #don't import methods

use base 'Exporter';
our @EXPORT  = qw( method_2_1 method_2_2 );

sub method_2_1
{
   print "method_2_1\n";
}

sub method_2_2
{
   print "method_2_2\n";
   module1::method_1_2();
}

1;

さて、私は何が起こっているのかわかると思いますが、これを一粒の塩と一緒に取ってください。use関数は事実上BEGINブロックであり、ブロックBEGINは解析されるとすぐに実行されるため、コードは実行順に次のようになります。

  1. perl解析を開始しますtest.pl
  2. 確認してuse module1;ロードmodule1.pmし、解析を開始します
  3. perlを確認use module2;module1.pmてロードmodule2.pmし、解析を開始します
  4. この時点では、の関数module1はまだ存在しないため、インポートできません
  5. 解析は続行されます

ボロディンが言ったことが、私に最善の解決策を教えてくれました。「@ module1::EXPORTは存在しません」。ここでの問題は、@EXPORT変数が存在しないことです。BEGINこれは、ブロックに入れることで修正できます。

module1.pm

package module1;

use strict;
use warnings;

use base 'Exporter';
BEGIN {
    our @EXPORT  = qw( method_1_1 method_1_2 );
}

use module2;

sub method_1_1
{
   print "method_1_1\n";
   module2::method_2_1();
}

sub method_1_2
{
   print "method_1_2\n";
}

1;

module2.pm

package module2;

use strict;
use warnings;

use base 'Exporter';
BEGIN {
    our @EXPORT  = qw( method_2_1 method_2_2 );
}

use module1;

sub method_2_1
{
   print "method_2_1\n";
}

sub method_2_2
{
   print "method_2_2\n";
   method_1_2();
}

1;

重要な注意:これらのケースのいずれにおいても、プロトタイプが尊重されるとは思いません(また、以前にコンパイルされmodule1てからプロトタイプがどのように機能するかわからないため、プロトタイプが存在することを知ることができません)。これは、プロトタイプを決して使用しないというさらに別の議論です。module2module1

于 2012-07-17T12:48:30.963 に答える