perlpragma - ユーザプラグマの書き方

目次


名前

perlpragma - ユーザプラグマの書き方


説明

プラグマとは, strictwarnings といったような, 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.

perlpragma - ユーザプラグマの書き方

索引

perlpragma - ユーザプラグマの書き方