perlpragma - ユーザプラグマの書き方
プラグマとは, strict や warnings といったような, Perl の
コンパイル時若しくは実行時のある状況に影響を与えるモジュールのことです.
Perl 5.10 ではもう組み込みのプラグマだけに制限されません; レキシカル
スコープでユーザ機能の振る舞いを偏光するユーザプラグマを作成することが
できます.
例えば, 算術演算のオーバーロードを行うクラスを作る必要があり,
それを以下のように use integer; 風に提供したいとします.
use MyMaths;
my $l = MyMaths->new(1.2);
my $r = MyMaths->new(3.4);
print "A: ", $l + $r, "\n";
use myint;
print "B: ", $l + $r, "\n";
{
no myint;
print "C: ", $l + $r, "\n";
}
print "D: ", $l + $r, "\n";
no myint;
print "E: ", $l + $r, "\n";
対応する出力:
A: 4.6
B: 4
C: 4.6
D: 4
E: 4.6
つまり, use myint; の影響下にある場所では加算は整数で行われ,
一方デフォルトではそうではなく(浮動小数点でもおこなわれる),
no myint; を使うことでデフォルトの振る舞いに戻すことも出来ます.
MyMaths パッケージの最小の実装は次のようになるでしょう:
package MyMaths;
use warnings;
use strict;
use myint();
use overload '+' => sub {
my ($l, $r) = @_;
# Pass 1 to check up one call level from here
if (myint::in_effect(1)) {
int($$l) + int($$r);
} else {
$$l + $$r;
}
};
sub new {
my ($class, $value) = @_;
bless \$value, $class;
}
1;
ユーザプラグマ myint を空のリスト () でロードすることで
その import が呼び出されないようにしています.
Perl のコンパイル時の動作は myint パッケージに対して作用します:
package myint;
use strict;
use warnings;
sub import {
$^H{myint} = 1;
}
sub unimport {
$^H{myint} = 0;
}
sub in_effect {
my $level = shift // 0;
my $hinthash = (caller($level))[10];
return $hinthash->{myint};
}
1;
プラグマは他のモジュールと同様のモジュールとして実装されているため,
use myint; は次のようになり
BEGIN {
require myint;
myint->import();
}
no myint; は次のようになります.
BEGIN {
require myint;
myint->unimport();
}
従って import 及び unimport ルーチンはユーザのコードの
コンパイル時 に呼び出されます.
ユーザプラグマは自身の状態をマジカルハッシュ %^H に書き出すことで
保存します, なのでこの2つのルーチンではその操作を行っています.
%^H に格納されている情報は optree に保存され, 実行時には
caller() 関数で返されるリストのインデックス値 10 で取得できます.
例示したプラグまでは, 取得は in_effect ルーチンにカプセル化
されていて, ユーザスクリプトの中でのプラグマの値を探すために
コールフレームを繰り登る回数をパラメータで受け取ります.
これはユーザのスクリプトの各行が呼び出されたときに $^H{myint}
の値を確定するために caller() を使っており, それによって
オーバーロードされた加算演算を実装しているサブルーチンに対して
適切なセマンティクスを提供しています.
optree はスレッド間で共有されています. これは optree がそれを
作った特定のスレッドよりも長生きである(つまりインタプリタの
インスタンスよりも)ことを意味し, そして実際に Perl のスカラは
optree に格納することはできません. 代わりにコンパクト形式が使われて
います, これは整数(符号付及び符号なし), 文字列 若しくは undef
のみを格納することができます - リファレンスやポインタ値は文字列化
されます. もし複数の値や複雑な構造を保存したいときには例えばpack
等でシリアライズする必要があります.%^H からのハッシュキーの
削除は記録され, undef 値を持っている存在するキーとは exists
によって区別できます.
山科 氷魚 (YAMASHINA Hio) <hio@hio.jp>
原典: perl VERSION 5.9.4. 翻訳日: 2007-06-23.