PerlIO の encoding layer の fallback ではまった

PerlIO の encoding layer は,$PerlIO::encoding::fallback という変数に fallback type を指定しておくことができるみたい。ということを PerlIO::encoding - encoding layer - metacpan.org 見て知った。encoding いじるのに PerlIO 使うのはなんとなく敬遠してたんだけど,fallback できるんなら使う価値あるんじゃね,と思って,POD にしたがってコードを書いてみた。

#!/usr/bin/perl

use strict;
use warnings;

use Encode;
use PerlIO::encoding;

local $PerlIO::encoding::fallback
    = Encode::FB_XMLCREF()
    ;

my $output = q{};

open my $handle, '>', \$output
    or die "open: $!";

binmode $handle, ':encoding(cp932)'
    or die "binmode: $!";

print {$handle} "foo" . chr(0x2764);

close $handle
    or die "close: $!";

print $output, "\n";

これを Perl 5.8.8 on RHEL 5.3 i386 で動かしてみた。

すると(ほぼ)POD に沿って書いたのに

Close with partial character at test.pl line 23.
Close with partial character.

と怒られてしまう(ちなみにこいつは結構強力な exception で eval でトラッピングできない)。


実は $PerlIO::encoding::fallback の指定しているところを,

local $PerlIO::encoding::fallback
    = Encode::XMLCREF()
    ;

とするとうまくいって,

foo❤

のようになる。


気づいたきっかけは,PerlIO::encoding でデフォルトで指定されてる $fallback

our $fallback =
    Encode::PERLQQ()|Encode::WARN_ON_ERR()|Encode::STOP_AT_PARTIAL();

のようになっていたこと。

Encode - character encodings in Perl - metacpan.org をみるとわかるように,FB_XMLCREFXMLCREF | LEAVE_SRC なんだけど,いろいろ試行錯誤してるとどうやら LEAVE_SRC が悪さをするらしい。

上記コードでは XMLCREF 単独で指定しているけど,PerlIO::encoding のソースに倣って STOP_AT_PARTIAL とかいう undocumented なフラグ等も指定しておいたほうがいいかもしんない。

気力と時間がないので原因は追ってない。環境依存なバグなのか POD のミスなのかバグなのか不明。Perl 5.10 だとうまくいくのかな?