2009-10-17
■ github+OmegaTを使ってPerl6ドキュメント日本語化プロジェクトを開始しました 
Perl6の日本語情報が少ないという現状を改善するべく、githubでPerl6ドキュメントの翻訳プロジェクトを始めました。OmegaT(フリーの翻訳メモリ)のプロジェクトを共同編集できるように調整してあるので、ぜひ協力をお願いします。
プロジェクトのURLはhttp://github.com/yamato/perl6-doc-jaです。READMEに翻訳作業の詳しい説明があります。作業内容は
- 未翻訳の文章を翻訳する
- 翻訳済みの文を推敲したり、誤訳を修正する
- glossary(用語集)を追加する
- docs2source.pl(pod→html変換スクリプト)を編集して翻訳ソースのHTMLにスタイルシートを付けたりする
などがあります。好みの作業を勝手にやっていただいて構いません。
2009-09-12
■ Dropboxみたいにファイルを監視したい 
フォルダを監視し、ファイル作成・変更・削除などのイベントを捕捉できるPerlモジュールは有るのだろうか。ということでCPANで探したところ、以下の3種類のモジュールを発見した。
File::Monitor
3つの中で最も完成度が高そうなモジュール。ドキュメントを読むと、Dropboxのようなプログラムも実装可能と書かれている。
- ファイルのstat関数の値を監視する
- ファイルが新規作成されたら通知する
- 監視中のファイルが削除されたら通知する
- ディレクトリ内にファイルが追加、または削除されたら通知する
イベントの通知はコールバック関数で受け取る。サブディレクトリを含めた監視が可能。
File::ChangeNotify
ファイルの作成・変更・削除の監視が可能。イベント通知はイベントループで受け取るタイプ。どうやらサブディレクトリの監視が出来ないようで、やや機能不足感がある。
Win32::FileSystem::Watcher
Win32APIを使用したモジュール。File::Monitorと同等のイベントを捕捉でき、サブディレクトリの監視も可能。ファイル名の変更が検出できたり、監視を非同期に実行できるのがポイント。ドキュメントが無いので、ソースを読んで理解する必要がある。
2009-08-17
■ 無名サブルーチンが原因で循環リファレンス発生 
MWT.pmの開発中、イベント処理で使う無名サブルーチンが原因で循環リファレンスが発生することが分かった。以下のようにForm(親)がButton(子)のリファレンスを保持している状態で起こる。
my $form = MWT::Form->new(); my $button = MWT::Button->new(); $form->controls->add($button); # 子を追加 # 参照関係 $form → $button
$formを操作するイベント処理を$buttonに追加すると循環参照になってしまう。
$button->click( sub { $form->text("hoge") } ); # クリックイベント追加 # 参照関係 $form → $button → 無名sub → $form
調べた結果、$formや$buttonのDESTROYがperl終了時まで呼ばれないことを確認した。これを回避するにはweak referenceを使う。こんな使い道があるとは思わなかった。
my $form_ref_copy = $form; Scalar::Util::weaken($form_ref_copy); $button->click( sub { $form_ref_copy->text("hoge") } );
問題は解決するが、かっこ悪い。どうしたものか。
2009-08-09
■ PERL_MAGIC_extの使い方 
PERL_MAGIC_extの使い方を覚えたので、詳しく紹介する。PERL_MAGIC_extはXS開発者向けに用意されているMAGICの一つで、XS(C言語)側でPerlの変数に情報を格納することが出来る。PERL_MAGIC_extはPerlコードからは全く見えず、ユーザー側からは普通の変数にしか見えない。また、Perlが内部的に変更することもない。XS開発者だけがアクセス出来る。
PERL_MAGIC_extを使ったオブジェクトの作り方
例として、以下のようなC言語の構造体my_mg_extを格納するオブジェクトを作るとする。ちなみに、本番でXSを開発する時でも独自の構造体を定義して、その中にバインディングしたいライブラリのポインタを格納することをお勧めする。ライブラリの型を直接MAGICに使ってしまうと、後で追加のデータが必要になった時に拡張できなくなってしまうからだ。
typedef struct { my_library* lib_ptr; } my_mg_ext;
そして、以下のようなコードと同等のXSを作るとする。
package Hoge; sub new { my $class = shift; my %hv; my $rv = \%hv; return bless $rv, ref($class) || $class; }
XSubは以下のようになる。
MODULE = Hoge PACKAGE = Hoge my_library* new(SV* klass) CODE: RETVAL = my_library_alloc(); OUTPUT: RETVAL void do_something(my_library* self) CODE: my_library_do_something(self);
この例ではオブジェクトの作成をtypemapで行う。typemapの内容は以下のようになる。
my_library* T_MY_MG_EXT
INPUT
T_MY_MG_EXT
if ( sv_isobject($arg) && sv_derived_from($arg, \"Hoge\") ) {
SV* ptr_sv;
ptr_sv = mg_find( SvRV($arg), PERL_MAGIC_ext )->mg_obj;
$var = INT2PTR( my_mg_ext*, SvIV(ptr_sv) )->lib_ptr;
}
else {
croak(\"Object is not of type Hoge\");
}
OUTPUT
T_MY_MG_EXT
{
HV* temp_hv; /* %hv */
SV* temp_rv; /* $rv */
SV* ptr_sv; /* ポインタ整数用 */
my_mg_ext* temp_ext;
Newx(temp_ext, 1, my_mg_ext); /* メモリ領域の確保 */
temp_ext->lib_ptr = $var; /* $var→RETVALに変換される */
temp_hv = newHV(); /* ハッシュ配列を生成 */
hv_magic(temp_hv, NULL, PERL_MAGIC_ext); /* temp_hv内にMAGIC構造体を作る */
/* temp_extのポインタを整数(IV)に変換し、新しいSVに格納する */
ptr_sv = newSViv( PTR2IV(temp_ext) );
/* mg_objに格納 */
mg_find( (SV*)temp_hv, PERL_MAGIC_ext )->mg_obj = ptr_sv;
/*
temp_hvのリファレンスを作る。 temp_hvのリファレンスカウントは
既に1なのでnewRV_noincを使用する
*/
temp_rv = newRV_noinc( (SV*)temp_hv ); /* my $rv = \%hv; */
sv_bless( /* bless $rv */
temp_rv,
gv_stashpv( /* blessするpackageを作る */
/* klassはXSub newの第一引数 */
sv_isobject(klass)
? sv_reftype(klass, TRUE) /* ref($class) */
: SvPV_nolen(klass), /* 文字列 */
TRUE
)
);
sv_2mortal(temp_rv); /* temp_rvはもう必要ないので解放するようにする */
SvSetSV($arg, temp_rv); /* スタックにセット */
}
そして、オブジェクトの解放は以下のようなデストラクタで行う。
MODULE = Hoge PACKAGE = Hoge void DESTROY(SV* self) PREINIT: SV* ptr_sv; my_mg_ext* temp_ext; my_library* my_lib; CODE: ptr_sv = mg_find( SvRV(self), PERL_MAGIC_ext )->mg_obj; temp_ext = INT2PTR( my_mg_ext*, SvIV(ptr_sv) ); my_lib = temp_ext->lib_ptr; my_library_free(my_lib); Safefree(temp_ext); SvREFCNT_dec(ptr_sv); /* 整数ポインタ用SVを解放 */ sv_unmagic( SvRV(self), PERL_MAGIC_ext ); /* MAGICを削除 */
ポイント
mg_objはSV*型
mg_objの型はSV*として定義されている。従って、voidポインタに変換して格納するという使い方は厳密には間違っている。C言語ではOKだが、C++ではコンパイルエラーになる。そのため、ポインタを整数に変換してからSVに保存する必要がある。
/* C++ではエラー */ mg_find( (SV*)temp_hv, PERL_MAGIC_ext )->mg_obj = (void*)temp_ext; /* 厳密なやり方 */ mg_find( (SV*)temp_hv, PERL_MAGIC_ext )->mg_obj = newSViv( PTR2IV(temp_ext) );
MAGIC変数は普通の変数として使える
PERL_MAGIC_extはPerlコードからは見えないので、生成したオブジェクトは以下のように普通の変数として扱える。
package Hoge; sub perl_data { my $self = shift; # 普通のハッシュリファレンスとして使える $self->{perl_data} = shift if @_; return $self->{perl_data}; } package main; my $hoge = Hoge->new(); $hoge->do_something(); # XSub $hoge->perl_data("foo"); # perl sub
2009-08-12追記
ポインタと整数の変換はPTR2IVでなくPTR2UVでもいいかもしれない。
2009-07-04
■ MWT.pm開発:イベント処理を実装 
MWT.pmでイベント処理が動くようになった。その代わり、MWT側のWSCbase.cppを改造することになってしまったが。
use strict; use warnings; use utf8; use MWT; binmode STDOUT, ":encoding(sjis)"; my $form = MWT::Form->new(); $form->text("タイトルです"); my $title = $form->text; print "title = $title\n"; $form->click( sub { # まだMouseEventArgsは未実装 my $sender = shift; my $title = $sender->text; print "クリックされました title = $title\n"; } ); $form->location(100, 100); $form->width(400); $form->height(300); MWT::Application->run($form);
