はじめてのSWIG2 (PerlからC++へ配列を渡してみよう)
今日はとことん攻めます。
題名の通りです。
Hige.cpp
char** argvを受け取ってループで出力する関数を追加します。
void Hige::printArray(char** argv) { int i = 0; while (argv[i]) { fprintf(stdout, "argv[%d] = %s\n", i,argv[i]); i++; } }
Hige.i
Perl の配列 ⇔ char** のtypemapを追加します。
これは SWIGのマニュアルを読むとこっそりとかいてあります。
// This tells SWIG to treat char ** as a special case %typemap(in) char ** { AV *tempav; I32 len; int i; SV **tv; if (!SvROK($input)) croak("Argument $argnum is not a reference."); if (SvTYPE(SvRV($input)) != SVt_PVAV) croak("Argument $argnum is not an array."); tempav = (AV*)SvRV($input); len = av_len(tempav); $1 = (char **) malloc*1; for (i = 0; i <= len; i++) { tv = av_fetch(tempav, i, 0); $1[i] = (char *) SvPV(*tv,PL_na); } $1[i] = NULL; }; // This cleans up the char ** array after the function call %typemap(freearg) char ** { free($1); } // Creates a new Perl array and places a NULL-terminated char ** into it %typemap(out) char ** { AV *myav; SV **svs; int i = 0,len = 0; /* Figure out how many elements we have */ while ($1[len]) len++; svs = (SV **) malloc(len*sizeof(SV *)); for (i = 0; i < len ; i++) { svs[i] = sv_newmortal(); sv_setpv((SV*)svs[i],$1[i]); }; myav = av_make(len,svs); free(svs); $result = newRV((SV*)myav); sv_2mortal($result); argvi++; }
test.pl
配列のリファレンスを渡してあげればOK。
sub main { my $hige = HatenaSwig::Hige->new; print $hige->toString . "\n"; my @array = ('hoge', 'huga'); $hige->printArray(\@array); }
結果
[test]$ ./test.pl Hige argv[0] = hoge argv[1] = huga
でもちょっと
これで Perl から配列を渡すことが出来るようになったのですが、C++プログラマからするとちょっと不満あり。
char** が NULL terminate されるってのはいろいろと不安だし安全な気がしません。
そこで先駆者のtakさんの成果物である MeCab を見てみると。
http://www.chasen.org/~taku/software/mecab/bindings.html
Perl側から配列のリファレンスを渡しています。(ここは同じ)
$m = new MeCab::Tagger (\@arg);
C++側では、おなじみの argc / argv 方式になっていて argc で配列の要素数が分かるようになっています。
// コンストラクタ, スクリプト言語からは, 文字列の配列として与える. Tagger (int argc, char *argv[]);
これの typemap を配布していないかなぁと探し回るも時間切れ。
こちらのほうが絶対賢いよなぁ。
あとちょっと気になるのが tak さん作成の hatenakeyword.hで
AutoLink::~AutoLink() {
delete buf; buf = 0;
delete pattern; pattern = 0;
alloc_size = 0;
size = 0;
*1:len+2)*sizeof(char *