はじめての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;

デストラクタで 0 代入をしているのですが、これはスクリプト固有のGCへの配慮でしょうか?

*1:len+2)*sizeof(char *