読者です 読者をやめる 読者になる 読者になる

ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Perl の Class::Accessor::Lvalue::Fast モジュールの使い方とか実装とか

Perl

Class::Accessor::Lvalue::Fast モジュールを使っているコードをしばしば業務で目にしつつも、ちゃんと体系だって理解してなかったので調べてみた。 自分用メモ程度の内容なので詳細はドキュメント参照ということで

Class::Accessor 系のモジュール

Class::Accessor::Lvalue::Fast クラスは、Class::Accessor クラスを拡張するもの。 ここでは、Class::Accessor クラスとそれを拡張するようなクラス群を Class::Accessor 系のクラスと呼ぶことにして、それらのクラスを提供するモジュールについて説明する (Class::Accessor::Lvalue::Fast モジュールに関連するもののみ)。

Class::Accessor モジュール

基本となるのは Class::Accessor クラス。 このクラスを親クラスとするサブクラスを作ると、サブクラスには Class::Accessor クラスから以下のものが提供される。

  • ハッシュリファレンスベースのインスタンスを生成するクラスおよびインスタンスメソッド new
  • インスタンスメソッド get, set
  • アクセサメソッドを作るためのクラスメソッド mk_accessors, mk_ro_accessors, mk_wo_accessors

new メソッドはハッシュリファレンスを引数に取ることができ、ハッシュリファレンスがある場合はそのコピーがインスタンスのベースになる。 引数がない場合は、空ハッシュのリファレンスをベースにインスタンスが生成される。

インスタンスメソッド set は、指定されたキーを使ってインスタンスのハッシュから値を取り出し、それを返す。 インスタンスメソッド get は、指定されたキーを使ってインスタンスのハッシュに値を格納する。

mk_accessor, mk_ro_accessors, mk_wo_accessors はクラスメソッドであり、このメソッドを実行することでアクセサメソッドとなるインスタンスメソッドが定義される。 引数として、生成されるアクセサメソッドの名前となる文字列のリストを取る。 さらにその名前がインスタンスのハッシュのキーにもなる。 mk_ro_accessors は読み取り専用のアクセサ、mk_wo_accessors は書き込み専用のアクセサを定義する。 定義されるアクセサは以下の形式になる。

$self->accessor_name( $key, @values )

mk_accessor メソッドの引数に渡した文字列の値が accessor_name になる。 @values が空の場合 getter となり、@values が 1 つ以上の値を持つ場合は setter になる。 @values として 2 つ以上の値を渡した場合は、それらの値を要素としてもつ配列へのリファレンスが値としてセットされる。 なお、このアクセサメソッドは make_accessor, make_ro_accessor, make_wo_accessor メソッドによって生成されるものであり、これらのメソッドは単に get または set メソッドを呼び出すようなメソッドを生成する。

サブクラスで set, get メソッドや make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドすることで、定義されるアクセサメソッドの動作を変更することができる。

Class::Accessor::Fast モジュール

Class::Accessor::Fast クラスは Class::Accessor クラスのサブクラス。 make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドし、get, set メソッドを使わずに、直接インスタンスのハッシュの値を読み書きするようなアクセサを生成する。

アクセサの動作が多少速くなるが、get, set メソッドのオーバーライドによる動作の変更ができなくなるという欠点もある。

Class::Accessor::Lvalue モジュール

Class::Accessor::Lvalue クラスは Class::Accessor クラスのサブクラス。 make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドし、アクセサメソッドに代入できるような文を書けるようになる。

# __PACKAGE__->mk_accessor( 'accessor' ) で作られたアクセサメソッドに対して以下のように代入の式を書ける
$obj->accessor = 120;

実装としては、アクセサメソッドを lvalue サブルーチンにしている。 get, set メソッドを使うために tie 関数を使っているので動作が遅くなっているようで、get, set メソッドを使う必要がない場合は Class::Accessor::Lvalue::Fast を使うことを公式的に薦めている。 tie 関数や lvalue サブルーチンに関しては後述。

Class::Accessor::Lvalue::Fast モジュール

Class::Accessor::Lvalue::Fast クラスは Class::Accessor::Fast クラスのサブクラス。 Class::Accessor::Lvalue クラスと同様に make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドし、アクセサメソッドに代入できるような文を書けるようになる。 ただし、Class::Accessor::Fast クラスと同様に get, set メソッドを介さずに直接インスタンスのハッシュを操作するアクセサメソッドが定義されるので、Class::Accessor::Lvalue クラスを使った場合よりもアクセサメソッドの動作が高速になる。

内部実装の話

内部実装で使われているものたち。

lvalue サブルーチン

サブルーチンの定義時に lvalue 属性をつけてやることで、(呼び出し側から見ると) メソッドへの代入ができるようになる、というもの。 Perl 5.6.0 からサポートされてるらしい。 便利っぽいけど下記のように書かれているので、あまりがしがし使わないほうがいいのかも。

Lvalue subroutines are still experimental and the implementation may change in future versions of Perl.

Lvalue subroutines (perlsub - perldoc.perl.org)

なお、Want モジュール を使うことで、lvalue 属性がつけられたメソッドへの代入としての呼び出しがされたのかそうでないのかを知ることができる。

tie 関数

tie()とはなにか?

ずばり、「オブジェクトではない、perlの組み込みデータタイプを裏でオブジェクト化する仕組み」のことです。それがなぜtieと呼ばれるかと言えば、変数を指定されたクラスに結びつけるから、です。

perl - tie()って何をtieするの? (404 Blog Not Found)

Class::Accessor::Lvalue モジュールでは、lvalue サブルーチンの戻り値として tie された変数を返すことで、アクセサメソッドを lvalue サブルーチンにしつつ、get, set メソッドを使ってハッシュを操作するということを実現している。