Perl - XSでDLLを呼び出す(その3)

今回は簡単なサブルーチンを実装して見る。

新しいモジュールを準備する

以下のコマンドで新しいモジュールを準備する。

> cd /d c:\xs
> h2xs -n Example2
> cd Example2

出来上がったExample2.xsは以下の内容である。
なお、行頭に ">" の付いた行は各エリアの説明である。

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#include "const-c.inc"

> ここにはC言語のコードを埋め込むことができる。
> コメントは /* ... */ で記述する。

MODULE = Example2       PACKAGE = Example2

> これ以降はXSの文法に則って記述する。
> コメントは "# "(シャープとスペース)で始める。

INCLUDE: const-xs.inc

> ここにXS構文で定義したサブルーチンを実装する。

単純な戻り値のサブルーチンの書き方

実装に入る前に、戻り値が1つだけの単純なサブルーチンの書式を概ね把握して置こう。
その書式は以下のようになる。

<戻り値のタイプ>
<サブルーチン名>(<引き数名列>)
INPUT:
    <引き数のタイプ> <引き数名>;
            :
            :
PREINIT:
    <内部変数定義>
INIT:
    <初期化コード>
CODE:
    <処理コード>
OUTPUT:
    RETVAL

上記の各フィールドの詳細は、今後に少しずつ説明して行く。
では早速、サブルーチンを実装して見よう。

渡された整数を返す

引き数で指定された整数をそのまま返すサブルーチンを実装する。

IV
echo_iv(n)
INPUT:
    IV n;
CODE:
    RETVAL = n;
OUTPUT:
    RETVAL

戻り値のタイプは整数(Integer Value)を表す "IV" である。
C言語の "int" と記述しても良い。
ただし、タイプ以外を書くことはできない。

例)IV # 整数を返す ← コメントさえ不可

ここで指定したタイプが、自動的に生成されるRETVAL変数のタイプを決定する。

今回は引き数名列が "n" 1つだけである。
INPUTフィールドに記述する "n" のタイプは戻り値と同様に "IV" である。
やはり "int" と記述しても同じ整数を意味する。
内部変数や初期化コードは不要なので、PREINITとINITフィールドはそれ自体を省略する。
CODEフィールドでは、サブルーチンの戻り値となるRETVAL変数に引き数の "n" を代入する。
OUTPUTフィールドではRETVAL変数を戻り値として指定する。

それではビルドを実行しよう。

> perl Makefile.pl
> make

上手くできただろうか。
テストのために以下のスクリプトを "c:\xs\Example2\TestEx2.pl" として保存する。

use ExtUtils::testlib;
use Example2;

sub TestEx2
{
    print Example2::echo_iv(123) . "\n";
    print Example2::echo_iv(456) . "\n";
    print Example2::echo_iv(789.012) . "\n";
    print Example2::echo_iv("345") . "\n";
    print Example2::echo_iv("678.901") . "\n";
    print Example2::echo_iv("234yen") . "\n";
    print eval {Example2::echo_iv()};
    print $@;
    print eval {Example2::echo_iv(567, 890)};
    print $@;
}

TestEx2;

上記を実行した結果は以下の通りである。

123
456
789
345
678
234
Usage: Example2::echo_iv(n) at TestEx2.pl line 12.
Usage: Example2::echo_iv(n) at TestEx2.pl line 14.

結果を見ると分かる通り、少数は整数に丸められ、文字列は可能な限り整数に変換される。
興味深いのは、不正な個数の引き数を指定したときに、サブルーチンの使用方法が終了メッセージとして得られる点だ。
それは、まるで以下のスクリプトを実行したかのようである。

package Example2;

sub echo_iv
{
    die "Usage: Example2::echo_iv(n)" if ($#_ != 0);
    return int($_[0]);
}

package main;

sub TestEx2
{
    # TestEx2.plと同じ内容
}

TestEx2;

次回はこの謎に迫って見ようと思う。