アクセッサの自動生成(AUTOLOAD編)

以前アクセッサを自動生成する方法を紹介していたのですが、今回はAUTOLOADを使用した場合を紹介します。
http://d.hatena.ne.jp/okamuuu/20100417/1271479559

AUTOLOADとは

ある名前空間に存在するメソッドを実行するように指示したのに、そのメソッドが定義されていない
場合にこのAUTOLOADを用意しておくと、代わりに実行します。

1. スクリプト

use strict;
use warnings;

eval {
    package Hoge;
    sub new { bless $_[1], $_[0] }
    sub AUTOLOAD {
        no strict 'vars';
        print $AUTOLOAD, 'がAUTOLOADメソッドを呼び出した!!', "\n"
    }

    sub DESTROY { print 'DESTROYメソッドが実行された!!', "\n" }

    1;
};

my $hoge = Hoge->new( { name => 'okamura', lang => 'jp' } );

$hoge->name; # nameメソッドが定義されていないので代わりにAUTOLOADが呼ばれる
$hoge->lang; # langメソッドが定義されていないので代わりにAUTOLOADが呼ばれる

exit; # $hogeの寿命がつきるので DESTROYが呼ばれる

2. 実行

:!perl %
Hoge::nameがAUTOLOADメソッドを呼び出した!!
Hoge::langがAUTOLOADメソッドを呼び出した!!
DESTROYメソッドが実行された!!

$AUTOLOADには本来実行したかったメソッド名を完全修飾名でとりだせます。

DESTROYメソッドが定義されていないのに、AUTOLOADメソッドを定義すると、デストラクションと同時にAUTOLOADメソッドを呼び出すのでこのようにDESTROYを定義しています。

AUTOLOADが読み込まれたタイミングでシンボルテーブルを作成

まだ定義されていないメソッドを実行した場合シンボルテーブルからそのメソッドを発見できないのでAUTOLOADを探します。

なので最初にAUTOLOADが呼ばれたタイミングでシンボルテーブルからそのメソッドを発見できる状態にして、2回目以降はAUTOLOADを呼ばずにシンボルテーブルを参照できるようにする、というサブルーチンが必要となるまで定義されない、というトリックを今回紹介します*1

1. スクリプト

#!/usr/bin/perl
#use strict;
#use warnings;

eval {

    package Hoge;

    sub new { bless $_[1], $_[0] }

    sub AUTOLOAD {
        #    print 'this is ', $AUTOLOAD;
        if ( $AUTOLOAD =~ m/.*::(.*)/ ) {
            if ( $1 eq 'DESTROY' ) { return 1 }
            print '(初めて呼ばれたのでシンボルテーブルを作成)', "\n";
            my $method = $1;
            *{$AUTOLOAD} = sub { shift->{$method} };
            goto &$AUTOLOAD;
        }
    }
    1;
};

my $hoge = Hoge->new( { name => 'okamura', lang => 'ja' } );

print $hoge->name, "\n";
print $hoge->name, "\n";
print $hoge->name, "\n";
print $hoge->lang, "\n";
print $hoge->lang, "\n";
print $hoge->lang, "\n";

実行

:!perl %
(初めて呼ばれたのでシンボルテーブルを作成)
okamura
okamura
okamura
(初めて呼ばれたのでシンボルテーブルを作成)
ja
ja
ja

ここで使っているgoto文ですが、引数が&NAMEです。

% perldoc -f goto

あまり使う事のないgotoですが、引数が&NAMEのものは特殊なのです。これはAUTOLOADで使われる前提なのですが、AUTOLOADメソッドでgoto &$AUTOLOAD;を実行するともう一度AUTOLOADを呼び出すのですが、その際に$AUTOLOADに呼び出し元としてHoge::AUTOLOADを格納することになるのか、といったらそうならないでHoge::nameとかを格納します、という使われ方です*2

と、このようにするとサブルーチンが必要となるまで定義されないというお話でした。

まとめ

AUTOLOADとgoto &NAMEは新人に説明しないでいいと思いました。社内で使ってる場所見たとこないし。。。

*1:遅延評価というやつでしょうか、どれぐらいうれしいかはよく理解していません。

*2:もうこのへんの説明はやっつけ仕事ですがなにか?