open()の実体

PerlIOが導入されてから,open()の実体はメソッドになっている。Cで実装されたPerlIOレイヤの仮想テーブルからメソッドを探索して,実際のopen()を行うのはそのPerlIOレイヤのopen()メソッドというわけだ。

open my $in, '<:scalar', \my $s;

このように,第二引数でIOレイヤを指定すると,実際にopen()を行うのはPerlIO::scalarのopen()メソッドである。ただし,このメソッドはCレベルで提供されているのでPerlから直接アクセスすることはできない。

IOレイヤを指定しない場合は,デフォルトのIOレイヤが使われる。これはWin32では:unix:crlf,コンパイラのstdioがバッファへ直接アクセスする機能を提供しているときは:stdio,どちらでもない場合は:unix:perlioである(さらに環境変数PERLIOおよびopenプラグマでデフォルトの挙動を変えることもできるが,ここでは詳細は省略)。

レイヤーを複数指定するときは,右が「より上位」と解釈される。

open my $in, '<:unix:perlio', $file;

たとえば上記のコードでは,まず:perlioのopen()が呼び出される。:perlioのopen()は下位のopen()を必要とするので,内部で下位のレイヤである:unixシステムコールを直接行う低レベルIOレイヤ)のopen()を呼び出す。
上記コードの:unixのさらに下位にはデフォルトレイヤが存在するのだが,:unixは下位のopen()を呼び出さないので,そのデフォルトレイヤは無視される。
この挙動は,指定したレイヤにopen()メソッドが存在するケースである。

:utf8(実際にはこれはダミーレイヤなので,メモリ上にレイヤの実体は作られないが)などのレイヤはopen()を持たないので,挙動が異なる。

open my $in, '<:unix:perlio:utf8', $file;

このとき,まず右からopen()を持つレイヤが検索される(この場合は:perlio)。そしてそのレイヤがopen()メソッドを実行し,作成されたPerlIOオブジェクトに対して,先ほどスルーしたopen()を持たないレイヤ(この場合は:utf8)をプッシュする(つまり内部でbinmode $in, ':utf8'相当を行う)。

したがって,open()を持たないレイヤはふつう右側に指定しなければならない。:perlioは直下(すぐ左)のレイヤのopen()を呼び出そうとするので,:utf8が指定されているとそのopen()を呼び出せず,:perlioのopen()を完成できないし,:unixのような低レベルレイヤは直下のレイヤを参照しないので,左側に何を指定されても無視してしまうからだ。したがって以下のような記法は誤りである。

open my $in, '<:utf8:perlio', $file; # *誤り

(PerlIO::Utilの提供するレイヤについてはこれが当てはまらないことがあるが,それはまた別エントリで書く)


まとめ:

  • Perlのopen()に渡すレイヤは左が下位で右が上位
  • open()メソッドは上位から検索される
  • 独自のopen()を持つレイヤは左に,open()を持たないレイヤは右に書く
    • :unix, :stdio, :perlio, :crlf, :mmap, :win32などはopen()を持つ
    • :utf8, :bytesなどはopen()を持たない