Practice of Programming

プログラム とか Linuxとかの話題

[Perl]Exporterとimportじゃなくて、gotoとcaller

某社の後輩に下記のブログの件は、普通はどうやって解決するのかと聞かれた(てか、後輩のブログだったようだ、なぜ言わないw)。
http://d.hatena.ne.jp/foosin/20090331/1238509129

僕も以前、悩んだことがあり、Exporterのドキュメントを見たら、まんま書いてたという。export_to_level。

sub import {
    strict->import;
    warnings->import;
    __PACKAGE__->export_to_level(1, @_);
}

で、まぁ、おしまい。

Perl のExportの仕組みは、perldoc -f use を見るとわかりやすいんだけど。

BEGIN { require Module; import Module LIST; }

LIST部分が、use Module (...)の、(...)の部分。インポートする関数や、グルーピングの名前とかを取ったりする。
このimportていうメソッドがイクスポートの役割を担っている。で、普通は継承しているExporterの、importが使われる。
そのため、自前でimportを実装すると、Exporterから継承しているimportが呼ばれないので、困ったことになる。
Exporterのimport内で、callerで呼び出し元のpackageを見ているので、->SUPER::importとかしても駄目。

でも、そんなことは想定されていて、Exporterのドキュメントでは、export_to_levelを使えと書いてある。

           package A;
           @ISA = qw(Exporter);
           @EXPORT_OK = qw ($b);

           sub import
           {
               $A::b = 1;
               A->export_to_level(1, @_);
           }

と、知ってたんだけど使ったことなかったんですが、これを書くために使ってみたら、普通にうまく行きました。
って、もっといろんなパターンが下記ですでに紹介されてました。
http://d.hatena.ne.jp/dayflower/20090330/1238399982


ちなみに、あんまり関係ないけど、importを呼びたくない場合は、() を渡します。
これも、perldoc -f useに書いてるけど、

use Module ();

That is exactly equivalent to

BEGIN { require Module }

昔、Mod_perl Developer's Cookbookを読んでて、メモリ節約(余計なimportをしない)のテクニックで紹介されてて知った。


で、本題は、ここのブクマコメント。元某社の後輩が書いているブクマコメント。
http://b.hatena.ne.jp/kamipo/20090401#bookmark-12768808

sub import {
    strict->import;
    warnings->import;
 
    goto &Exporter::import;
}

さっき書いたとおり、Exporterのimportは呼び出し元みてるから駄目じゃないんかと思ったけど、
gotoって、呼び出し元が、呼んだところにならないんですね。へぇ〜。

sub w {
  print join(" ", caller),"\n"
}

package Hoge;

sub hoge{
  goto &main::w
};

package Fuga;

sub fuga {
  goto &main::w
}

package Main;

Fuga->fuga;
Hoge->hoge;

実行結果は、

Main - 19
Main - 20

知らんかった。だいぶびっくりした。
まぁ、goto使った事は無いけども

# 本題のが短くなってしまった