euc-jpNAME

perlcall - C からの Perl 呼び出し規約

DESCRIPTION

このドキュメントの目的は、どのようにして Perl のサブルーチンを C から 直接呼び出すか、つまり コールバック の書き方を示すことです。

コールバックを記述するための Perl から提供される C の インターフェースについての議論とは別に、本ドキュメントでは実践的な インターフェースを例で示します。 それに加え、コールバックをコーディングするための幾つかの テクニックがカバーされます。

必要に応じて、例を示すようにしてあります。

記述したテクニックは、Perl プログラムを C のプログラムに埋め込むときにも 適用できますが、それはこのドキュメントの本来の目的ではありません。 Perl を組み込むことに特化したその他の詳細や考慮しなければならないことが あります。 C に Perl を埋め込むことの詳細は perlembed を参照してください。

このドキュメントの先に進む前に、perlxsperlguts という 二つのドキュメントを読んでおいたほうが良いでしょう。

CALL_ 関数

これらの性質は例を使って説明するのが簡単であるにも関らず、あなたは まず幾つかの重要な定義を知ることが必要です。

Perl には、Perl のサブルーチンを呼ぶことを許している C の関数が たくさんあります。 それを以下に示します。

    I32 call_sv(SV* sv, I32 flags);
    I32 call_pv(char *subname, I32 flags);
    I32 call_method(char *methname, I32 flags);
    I32 call_argv(char *subname, I32 flags, register char **argv);

中心となる関数は call_sv です。 他の全ての関数は特別な状況で Perl サブルーチンを呼び出しやすくするための単純なラッパーです。 最終的にはこれらの関数はすべて call_sv を呼び出して Perl サブルーチンを起動します。

関数 call_* は Perl に対するオプションのビットマスクを 渡すのに使われる flags 引数を取ります。 このビットマスクはそれぞれの関数で全く同じように働きます。 設定可能なビットマスクは "FLAG VALUES" で述べられています。

関数のそれぞれに対する説明を始めましょう。

call_sv

call_sv は二つのパラメータをとります。 第一引数が SV* である sv です。 これにより、呼び出す Perl サブルーチンを C の文字列(この場合 最初に SV に変換されます)としても、サブルーチンに対する リファレンスとしても指定することができます。 セクション "Using call_sv" で、 どのように call_sv を使えるかを示します。

call_pv

関数 call_pvcall_sv と似ていますが、 call_pv("fred", 0) のように呼び出したい Perl サブルーチンを 指定する第一引数に C の char * が来ることを期待しているという点が 異なります。 呼び出したいサブルーチンが別のパッケージに置かれている場合、 文字列にパッケージ名を含め "pkg::fred" のようにします。

call_method

関数 call_method は Perl のクラスからメソッドを呼び出すのに使われます。 methname というパラメータは呼び出すメソッドの名前に対応します。 メソッドの所属するクラスは、パラメータリストではなく Perlのスタックで渡されるということに注意してください。 このクラスは、クラスの名前(静的メソッドに対する場合)であっても オブジェクトに対するリファレンスであってもかまいません。 静的メソッドと仮想メソッドに関する詳細は perlobj を、call_method を 使った例は "Using call_method" を参照してください。

call_argv

call_argv はパラメータ subname に格納された C 文字列で 指定された Perl サブルーチンを呼び出します。 これはまた、flags パラメータを取ります。 最後のパラメータ argv は、Perl サブルーチンに渡されるパラメータである C 文字列の、NULL で終端されたリストから構成されます。 "Using call_argv" を参照してください。

上記の関数はすべて整数値を返します。 これは、Perl サブルーチンが返したアイテムの数です。 サブルーチンから返された実際のアイテムは Perl スタックに 格納されています。

一般的な規則として、あなたは いつでも これらの関数の戻り値を チェックすべきです。 あなたが Perl サブルーチンから返される値として、 ある特定の数だけを期待していたとしても、誰かが予期しないなにかを 行うことを止める手だてはないので -- 自分は警告されていない、とは 云わないでください。

フラグの値

すべての call_* 関数のパラメータ flags は、呼び出しコンテキストを示す G_VOID, G_SCALAR, G_ARRAY の一つに、以下に定義するその他の G_* シンボルの 組み合わせのビットマスクを「論理和」で組み合わせて用います。

G_VOID

Perl のサブルーチンを無効コンテキストで呼び出します。

このフラグには二つの効果があります。

  1. サブルーチンの呼び出しが無効コンテキスト(もし wantarray を実行すると その結果は未定義値となります)において実行されることを示します。

  2. サブルーチンから実際に返されるものがなにもないことを保証します。

関数 call_* の戻り値は Perl のサブルーチンがアイテムを幾つ 返したのかを示します--この例の場合は、0 となります。

G_SCALAR

Perl のサブルーチンをスカラコンテキストで呼び出します。 これはすべての call_* 関数に対するデフォルトのコンテキストの設定です。

このフラグには二つの効果があります。

  1. サブルーチンに対して、スカラコンテキストで実行されるということを 示します(wantarray を実行した場合、結果は偽となります)。

  2. サブルーチンから実際に返されるものはスカラだけであるということを 保証します。 もちろん、サブルーチンは wantarray を無視し、リストを 返すこともできます。 そうした場合、そうしたリストの最後の要素だけが返されることになります。

関数 call_* により返された値は、Perl サブルーチンが幾つの アイテムを返したかということを示します - この場合、0 か 1 かの いずれかになります。

0 の場合、G_DISCARD フラグが指定されたということです。

1 の場合、Perl サブルーチンが実際に返したアイテムは Perl スタックに 積まれます - スタックにあるこの値にどのようにアクセスするかはセクション Returning a Scalar にあります。 Perl サブルーチンが幾つのアイテムを返したかに関係なく、最後の一つだけが スタック上でアクセス可能であるということを忘れないでください - つまり、 ただ一つの要素からなるリストのように、値が一つだけ置かれるのです。 他のすべてのアイテムは、call_* 関数から制御が戻ったときには 存在しません。 この振る舞いについての例はセクション Returning a list in a scalar context にあります。

G_ARRAY

Perl サブルーチンをリストコンテキストで呼び出します。

G_SCALAR を指定したときと同様、このフラグにも二つの効果があります。

  1. サブルーチンが、リストコンテキストで実行されることを示します (wantarray を実行すると、その結果は真となります)。

  2. 関数 call_* から制御が戻ったときに、サブルーチンから返された すべてのアイテムがアクセス可能であることを保証します。

関数 call_* から返される値は、Perl サブルーチンがアイテムを 幾つ返したかを示すものです。

0 の場合、G_DISCARD フラグが指定されたということです。

0 以外の場合、サブルーチンが返したアイテムの個数となります。 これらのアイテムは Perl スタックに置かれます。 セクション Returning a list of values には G_ARRAY フラグを使った例と、 Perl スタックに置かれたアイテムをアクセスする仕組みの例があります。

G_DISCARD

デフォルトでは、関数 call_* は Perl サブルーチンの返したアイテムを スタックに置きます。 こういったアイテムに用がない場合、このフラグを設定することによって Perl が自動的にこれらのアイテムを取り除くようにすることを指定できます。 このフラグを使った場合でも、 Perl サブルーチンに対して G_SCALAR か G_ARRAY のいずれかをさらに 指定することで、(Perl サブルーチンが実行される)コンテキストを 指示できるということに注意してください。

このフラグを設定しなかった場合、なんらかの一時変数(たとえば、Perl サブルーチンに渡されたパラメータであるとかサブルーチンからの戻り値)を 自分自身で破棄することを確実に行うことが非常に重要です。 Returning a Scalar というセクションでは、これら一時変数をどのようにして 陽に破棄するかを説明しており、 Using Perl to dispose of temporaries というセクションではこの 問題を無視できる特定の場所や、Perlに後始末をさせることについて 述べています。

G_NOARGS

call_* の一つを使って Perl サブルーチンを呼び出したときはいつでも、 サブルーチンに対してパラメータが渡されたということが デフォルトで仮定されます。 Perl サブルーチンに対してなんのパラメータも渡さないという場合、この フラグをセットすることで時間を少々節約することができます。 これは Perl サブルーチンに対して、@_ を生成しないという 効果を持っています。

このフラグにより提供される機能が直接的なもののように見えるにも関らず、 これを使うのはそうすべき適当な理由があるときのみにすべきでしょう。 このように用心する理由は、G_NOARGS フラグを指定した場合であっても、 呼び出された Perl サブルーチンがパラメータを渡されたと 思い込んでいる可能性があるからです。

事実、起こりうる可能性として、呼び出した Perl サブルーチンが以前の Perl サブルーチンの配列 @_ にアクセスできてしまうというものがあります。 これは、call_* 関数を実行するコード自身が、他の Perl サブルーチンから 呼ばれたときに起こり得ます。 以下のコードはこれを説明するものです。

    sub fred
      { print "@_\n"  }

    sub joe
      { &fred }

    &joe(1,2,3);

この結果は次のようになります。

    1 2 3

ここで起こったことは、fredjoe に属する配列 @_ に アクセスしているということです。

G_EVAL

呼び出す Perl サブルーチンを、die を陽に呼び出したり、何かが 存在しなかったことにより即座に終了させることを可能にします。 デフォルトでは、そういったイベントが起こった場合にはプロセスは即座に 終了します。 しかしこの種のイベントをトラップしたいのなら、G_EVAL フラグを指定します。 それによって、サブルーチンの呼び出しを eval { } で囲むようになります。

call_* 関数から戻ってきたときは、いつでも通常の Perl スクリプトで そうするように変数 $@ をチェックする必要があります。

call_* から返された値は、他に指定したフラグとエラーが 起きたかどうかに依存します。 以下に起こりうるすべてのケースを挙げます。

G_EVAL の使用についての詳細は "Using G_EVAL" を参照してください。

G_KEEPERR

上述の G_EVAL フラグを使うと、常に $@ を設定します: エラーがなければ クリアし、呼び出したコードにエラーがあればエラーを記述します。 これは、可能性のあるエラーを扱おうとするのが目的なら求めているものですが、 単にエラーをトラップしてプログラムの残りを侵害するのを止めたいだけかも しれません。

このシナリオは、デストラクタの中から呼ばれるようなコード、非同期な コールバック、シグナルハンドラといったもので最も有用です。 このような状況では、呼び出されたコードは周りの動的な内容とは ほとんど関係がなく、メインプログラムは、例え理性的に扱えなくても、 呼び出されたコードのエラーから隔離される必要があります。 また、こうするのは __DIE____WARN__ フックのコードや、 tie 関数で有用かもしれません。

G_KEEPERR フラグは、実装したコードを使用する call_* において G_EVAL と、または eval_sv と組み合わせて使われます。 このフラグは G_EVAL が使われていないときにはなんの効果も持ちません。

G_KEEPERR が使われると、呼び出されたコードのエラーは通常通り 呼び出しを終了させ、(G_EVAL での通常通り)エラーは呼び出しを超えて 伝搬しませんが、$@ は設定されません。 エラーが警告に変換される代わりに、"\t(in cleanup)" という文字列が 前置されます。 これは no warnings 'misc' を使って無効にできます。 エラーがなければ、$@ はクリアされません。

G_KEEPERR フラグは内側の eval に伝搬しないことに注意してください; これらは未だに $@ を設定します。

G_KEEPERR フラグは Perl のバージョン 5.002 で導入されました。

このフラグを使うことが正当である状況の例が "Using G_KEEPERR" にあります。

コンテキストを決定する

先に説明したように、wantarray を使ってサブルーチンが実行されている コンテキストを判定することができます。 これと同じことが、GIMME_V マクロを使うことでも可能です; このマクロは リストコンテキストで呼ばれた場合には G_ARRAY を、スカラコンテキストで 呼ばれた場合には G_SCALAR を、無効コンテキストの場合(たとえば戻り値が 使われないとき)には G_VOID を返します。 このマクロの古いバージョンは GIMME という名前で、無効コンテキストのときに G_VOID の代わりに G_SCALAR を返していました。 GIMME_V マクロの使用例はセクション "Using GIMME_V" にあります。

定義の話は十分でしょう! いくつか例を示します。

Perl では、Perl スタックをアクセスする手助けのためのマクロをたくさん 用意しています。 Perl の内部ルーチンとのインターフェースには、 常にこれらのマクロを使うようにすべきでしょう。 将来 Perl に施される変更に対しても、これを使うことで、コードの方への 影響が少なくなるはずです。

もう一つ余計なことを言っておくと、最初のいくつかの例では、 call_pv 関数だけを使っています。 これは、単にみなさんがこの話題に入り込みやすいようにと 考えただけのことです。 call_pvcall_sv のどちらも使える場合、常に call_sv を 使うことを試してください。 詳しくは "Using call_sv" を参照してください。

引数なし、戻り値なしの場合

最初の明快な例は、プロセスの UID を出力するために Perl サブルーチン PrintUID を呼びだします。

    sub PrintUID
    {
        print "UID is $<\n";
    }

そして、これを呼び出す C 関数です

    static void
    call_PrintUID()
    {
        dSP;

        PUSHMARK(SP);
        call_pv("PrintUID", G_DISCARD|G_NOARGS);
    }

ね、単純でしょう?

この例についていくつか注意点をあげておきましょう:

  1. 今のところ、dSPPUSHMARK(SP) は無視してください。 これについては、次の例でお話します。

  2. PrintUID に引数を渡しませんので、G_NOARGS を指定できます。

  3. PrintUID から返されるものは、特に必要ありませんので G_DISCARD を 指定しています。 PrintUID が何か値を返すように変更されても、 G_DISCARD を指定しておくと call_pv から制御が戻るときにその値が 捨てられることになります。

  4. call_pv が使われていますから、Perl サブルーチンは C の文字列として 表現されています。 この場合サブルーチン名はコードに「ハードコーディング」されることになります。

  5. G_DISCARD を指定しましたので、call_sv からの戻り値をチェックする 必要はありません。 常に 0 となります。

引数を渡す

さて、今度はもう少し込み入った例です。 今回は、二つの引数として、文字列 ($s) と整数 ($n) をとる Perl サブルーチン LeftString を 呼び出してみましょう。 このサブルーチンでは、単純に文字列の最初の $n 文字を出力します。

Perl サブルーチンは以下のようなものです:

    sub LeftString
    {
        my($s, $n) = @_;
        print substr($s, 0, $n), "\n";
    }

LeftString を呼ぶための C の関数は、以下のようになります:

    static void
    call_LeftString(a, b)
    char * a;
    int b;
    {
        dSP;

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(a, 0)));
        XPUSHs(sv_2mortal(newSViv(b)));
        PUTBACK;

        call_pv("LeftString", G_DISCARD);

        FREETMPS;
        LEAVE;
    }

C の関数 call_LeftString の注意点を示します。

  1. パラメータは、Perl スタックを使って Perl サブルーチンに渡されます。 これが、dSP で始まり PUTBACK 行で終わるコードの目的です。 dSP はスタックポインタのローカルなコピーを宣言します。 このローカルなコピーは 常に SP として アクセスするようにすべきです。

  2. Perl スタックに何かを置こうとする場合には、どこにおけばよいかを 知る必要があります。 これが、マクロ dSP の目的です -- Perl スタックポインタの ローカルな コピーを宣言し、初期化します。

    この例で使う他のすべてのマクロは、このマクロの使用を前提にしています。

    Perl サブルーチンを直接 XSUB 関数から呼ぶ場合はこの規則の例外です。 この場合 dSP マクロを陽に使う必要はありません -- 自動的に宣言されます。

  3. スタック上に積まれるパラメータはすべて、PUSHMARK マクロと PUTBACK マクロで囲まなければなりません。 この 二つのマクロは、 この文脈では、自動的にプッシュした引数の数を数えるために使われます。 これによりPerl がサブルーチンに対して @_ 配列を作るときに、 その大きさがわかるという仕組みになっているのです。

    PUSHMARKマクロは、Perl に対して、内的にその時点のスタックポインタに 注目するように伝えます。 (例 1 の場合のように) 引数を渡さない場合であっても、call_* 関数を 呼ぶ場合には、その前に PUSHMARK マクロを呼ばなくてはなりません -- Perl からすると、引数が ないことを知る必要があるのです。

    少し先へ飛んで、PUTBACK マクロは、グローバルなスタックポインタを、 先に作ったローカルなものに合わせます。 これを行わないと、どこに引数を置いたかを call_pv が 判断することができません -- ここまで、すべてのスタックポインタの 操作は、ローカルなものに対して行なってきたのであって、 グローバルなものは触っていなかったのです。

  4. 元に戻って、次に XPUSH を引数分呼んでいます。 ここで、実際に引数がスタックに積まれます。 今回の場合、文字列と整数を積んでいます。

    XPUSH マクロがどのように動作するのかについて詳しくは、 "XSUBs and the Argument Stack" in perlguts を参照してください。

  5. 私たちはここで一時的な値を(sv_2motral() の呼び出しによって)作り出したので、 Perl のスタックを調整することと mortal な SV を消去する必要が生じます。

    これが関数の先頭に

        ENTER;
        SAVETMPS;

    を置き、

        FREETMPS;
        LEAVE;

    を関数の末尾に置く理由です。 ENTER/SAVETMPS のペアは作成した一時変数に対する境界を 生成します。 つまり、私たちが取り扱う一時変数がこれらの呼び出しの後で 生成されたものに限定されるだろうということです。

    FREETMPS/LEAVE のペアは Perl のサブルーチンが返す任意の値を扱い (次の例を参照してください)、それに加えて私たちが作成した mortal な SV を ダンプします。 コードの先頭で ENTER/SAVETMPS を使うことによって、mortals 以外の ものが削除されないようにします。

    これらのマクロは、Perl を使っているときに {} による ローカル変数のスコープの制限に似たような働きをすると考えてください。

    これらのマクロを使った別の例については セクション "Using Perl to Dispose of Temporaries" を 参照してください。

  6. ここまでで、LeftStringcall_pv 関数を介して呼ぶことが できます。 今回指定しているフラグは G_DISCARD のみです。 今回 Perl サブルーチン に 二つのパラメータを渡しますので、G_NOARGS は 指定していません。

スカラを返す

さて、Perl サブルーチンからの戻り値を扱う例を見てみましょう。

ここに挙げる Perl サブルーチンは、二つの整数引数をとって単にその和を 返す Adder というものです。

    sub Adder
    {
        my($a, $b) = @_;
        $a + $b;
    }

Adder からの戻り値を扱う必要がありますので、C 関数側は今回、 多少複雑になります。

    static void
    call_Adder(a, b)
    int a;
    int b;
    {
        dSP;
        int count;

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSViv(a)));
        XPUSHs(sv_2mortal(newSViv(b)));
        PUTBACK;

        count = call_pv("Adder", G_SCALAR);

        SPAGAIN;

        if (count != 1)
            croak("Big trouble\n");

        printf ("The sum of %d and %d is %d\n", a, b, POPi);

        PUTBACK;
        FREETMPS;
        LEAVE;
    }

ここでの注意点は、

  1. 今回は、フラグとして G_SCALAR だけを使用しています。 これは、@_ 配列が作られ、Adder から返された値は、call_pv の 呼び出し後も存在することを表わします。

  2. SPAGAIN マクロの目的は、ローカルなスタックポインタを リフレッシュすることです。 call_pv の呼び出しの間に再配置された Perl スタックへメモリ配置を 行なうことができますから、これが必要になります。

    コードの中で Perl スタックポインタを利用する場合には、call_* 関数や 他の Perl の内部関数を利用するときには必ず、SPAGAIN を使って ローカルなポインタをリフレッシュしなくてはなりません。

  3. Adder から返されるのは、単一の値のみのはずですが、いずれにしても call_pv からの返却値をチェックするのが良いでしょう。

    単一の値を期待するのは、一つであることを知っていることとは違います。 誰かが Adder を改造してリストを返すようにし、その可能性を チェックしていなかったときにこの操作を行なってしまうと、Perl スタックの 状態は矛盾したものになってしまいます。 こんなことは誰も望みはしないでしょう。

  4. スタックから、返却値をポップするのに、ここでは POPi マクロを 使用しています。 ここでは整数が必要であったため、POPi を使用したのです。

    返された値の型に応じて、以下の POP マクロを使用することができます。

        POPs        SV
        POPp        pointer
        POPn        double
        POPi        integer
        POPl        long
  5. 最後の PUTBACK は、関数を抜ける前に、Perl スタックを矛盾の無い 状態にしておくために使用しています。 これは、POPi でスタックから返却値を取り出したときに、ローカルな スタックポインタのみを更新するため、必要となります。 先の例で説明したように PUTBACK は、グローバルなスタックポインタを ローカルなスタックポインタの値にあわせるように設定します。

値のリストを返す

さて、今度は、先の例を改造して、二つの引数の和と差の両方を返すように してみましょう。

Perl サブルーチンは

    sub AddSubtract
    {
       my($a, $b) = @_;
       ($a+$b, $a-$b);
    }

のようになり、C 関数は

    static void
    call_AddSubtract(a, b)
    int a;
    int b;
    {
        dSP;
        int count;

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSViv(a)));
        XPUSHs(sv_2mortal(newSViv(b)));
        PUTBACK;

        count = call_pv("AddSubtract", G_ARRAY);

        SPAGAIN;

        if (count != 2)
            croak("Big trouble\n");

        printf ("%d - %d = %d\n", a, b, POPi);
        printf ("%d + %d = %d\n", a, b, POPi);

        PUTBACK;
        FREETMPS;
        LEAVE;
    }

call_AddSubtract が以下のように呼ばれた場合、

    call_AddSubtract(7, 4);

出力はこうなります。

    7 - 4 = 3
    7 + 4 = 11

注意点

  1. リストコンテキストが必要なので、G_ARRAY を使用します。

  2. スタックから 二つの値を取り出すために今回 二つの POPi を 使っていることは驚くに値しないでしょう。 それよりも注目すべきは、POP* マクロを使ってスタックから 取り出したときに 逆順 となることです。

スカラコンテキストでリストを返す

前のセクションにあった Perl サブルーチンは、この例のように スカラコンテキストで呼ばれていました。

    static void
    call_AddSubScalar(a, b)
    int a;
    int b;
    {
        dSP;
        int count;
        int i;

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSViv(a)));
        XPUSHs(sv_2mortal(newSViv(b)));
        PUTBACK;

        count = call_pv("AddSubtract", G_SCALAR);

        SPAGAIN;

        printf ("Items Returned = %d\n", count);

        for (i = 1; i <= count; ++i)
            printf ("Value %d = %d\n", i, POPi);

        PUTBACK;
        FREETMPS;
        LEAVE;
    }

別の変更点は、call_AddSubScalar が Perl サブルーチンから返された アイテムの数を出力するようになり、サブルーチンの戻り値 (単純化のために、ここではこれは整数と仮定します)を 出力するようになったと言うことです。 ですから、call_AddSubScalar

    call_AddSubScalar(7, 4);

のように呼び出され、以下のような出力を行います。

    Items Returned = 1
    Value 1 = 3

この場合に注意すべき点は、サブルーチンからはリストの最後にある アイテムだけが返されるということです。 AddSubtract は実際には call_AddSubScalar に返しているのと同じです。

引数リストを使って Perl からデータを返す

引数リストを使って直接値を返すことも可能です -- ただし、これが実際に 望ましい方法であるか否かについては、まったく別の問題です。

以下の Perl サブルーチン Inc は二つの引数をとり、それぞれを インクリメントします。

    sub Inc
    {
        ++ $_[0];
        ++ $_[1];
    }

そして、これを呼び出す C 関数です。

    static void
    call_Inc(a, b)
    int a;
    int b;
    {
        dSP;
        int count;
        SV * sva;
        SV * svb;

        ENTER;
        SAVETMPS;

        sva = sv_2mortal(newSViv(a));
        svb = sv_2mortal(newSViv(b));

        PUSHMARK(SP);
        XPUSHs(sva);
        XPUSHs(svb);
        PUTBACK;

        count = call_pv("Inc", G_DISCARD);

        if (count != 0)
            croak ("call_Inc: expected 0 values from 'Inc', got %d\n",
                   count);

        printf ("%d + 1 = %d\n", a, SvIV(sva));
        printf ("%d + 1 = %d\n", b, SvIV(svb));

        FREETMPS;
        LEAVE;
    }

call_pv からリターンした後で、スタック上にプッシュされた 二つのパラメータにアクセスするために、これらのアドレスを記録しておく 必要があります -- このため 二つの変数 svasvb を使っています。

これらの値を保持する Perl スタックのエリアは、call_pv から 制御が戻るときまでに何ものかによって破壊されていることが十分に 有り得るので、こういった操作が必要になるのです。

G_EVAL を使う

今度は G_EVAL を使う例です。 以下は、二つの引数の差を計算する Perl サブルーチンです。 結果が負になった場合、このサブルーチンは die を呼びます。

    sub Subtract
    {
        my ($a, $b) = @_;

        die "death can be fatal\n" if $a < $b;

        $a - $b;
    }

これを呼ぶ C の方は以下のようになります

    static void
    call_Subtract(a, b)
    int a;
    int b;
    {
        dSP;
        int count;

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSViv(a)));
        XPUSHs(sv_2mortal(newSViv(b)));
        PUTBACK;

        count = call_pv("Subtract", G_EVAL|G_SCALAR);

        SPAGAIN;

        /* Check the eval first */
        if (SvTRUE(ERRSV))
        {
            printf ("Uh oh - %s\n", SvPV_nolen(ERRSV));
            POPs;
        }
        else
        {
            if (count != 1)
               croak("call_Subtract: wanted 1 value from 'Subtract', got %d\n",
                        count);

            printf ("%d - %d = %d\n", a, b, POPi);
        }

        PUTBACK;
        FREETMPS;
        LEAVE;
    }

call_Subtract が以下のように呼ばれると

    call_Subtract(4, 5)

以下のように表示されます。

    Uh oh - death can be fatal

注意点

  1. die を捕捉するために、G_EVAL フラグを使用しました。 このフラグを使用しないと、サブルーチン Sbutract 中にある die 文の場所でプログラムは途中終了してしまいます。

  2. このようなコードは

        if (SvTRUE(ERRSV))
        {
            printf ("Uh oh - %s\n", SvPV_nolen(ERRSV));
            POPs;
        }

    Perl の書くところの以下と直接に等価です

        print "Uh oh - $@\n" if $@;

    PL_errgv は、エラーを保持するシンボルテーブルのエントリーを指し示す、 型 GV * の perl のグローバル変数です。 ですから ERRGV は、Cプログラムににおいて $@ と等価なものを 参照します。

  3. スタックは SvTRUE(ERRGV) が真であるブロックの中で POPs を 使ってポップされていることに注意してください。 これは、関数 call_* が G_EVAL|G_SCALAR をつけて起動された場合に エラーを返していて、スタックトップに undef 値があるときには常に 必要となります。 エラーを検出した後でプログラムを継続させたいので、undef を 取り除いてスタックを片付けることが重要なのです。

G_KEEPERR を使う

以下の滑稽な例について考えてみましょう; ここでは、先の例での XS バージョンの call_Subtract をデストラクターの中で使っています。

    package Foo;
    sub new { bless {}, $_[0] }
    sub Subtract {
        my($a,$b) = @_;
        die "death can be fatal" if $a < $b;
        $a - $b;
    }
    sub DESTROY { call_Subtract(5, 4); }
    sub foo { die "foo dies"; }

    package main;
    {
        my $foo = Foo->new;
        eval { $foo->foo };
    }
    print "Saw: $@" if $@;             # should be, but isn't

この例では、eval {} の内側で起きたエラーを認識するのに失敗します。 その理由はこうです: call_Subtract のコードは外側の中かっこブロックを抜ける 際にPerlが一時変数を片付ける最中に実行されます; そして call_Subtract が G_EVAL フラグを使った call_pv を使って実装されているので $@ を リセットするからなのです。 この結果は $@ を最も外側の検査での失敗で、エラートラップに 失敗するということです。

G_KEEPERR フラグを追加して call_Subtract の中にある call_pv 呼び出しを

        count = call_pv("Subtract", G_EVAL|G_SCALAR|G_KEEPERR);

のようにすると、エラーを保存し、信頼性のあるエラーハンドリングを 取り戻します

call_sv を使う

これまでの例では、Perl サブルーチンの名前を C から呼ばれるように 扱ってきました。 ですが、ときには Perl スクリプトの中から Perl サブルーチンの名前を 指定することができないと困ることがあります。

のような Perl コードを考えてみましょう。

    sub fred
    {
        print "Hello there\n";
    }

    CallSubPV("fred");

これは CallSubPV を定義する XSUB の一部です。

    void
    CallSubPV(name)
        char *  name
        CODE:
        PUSHMARK(SP);
        call_pv(name, G_DISCARD|G_NOARGS);

今のところは、これで良いのかもしれません。 問題は、Perl サブルーチンが文字列でしか表現できないということです。 Perl 4 ではこれで十分なのですが、Perl 5 ではサブルーチンへの リファレンスや名前を持たないサブルーチンが許されています。 そういった場合に、call_sv が役に立つのです。

次の CallSubSv のコードは、CallSubPV と同一ですが、引数 name を SV* で定義し、call_pv の代わりに call_sv を使っています。

    void
    CallSubSV(name)
        SV *    name
        CODE:
        PUSHMARK(SP);
        call_sv(name, G_DISCARD|G_NOARGS);

fred を呼ぶのに SV を使っていますから、Perl 側では以下のような記法が すべて可能になります:

    CallSubSV("fred");
    CallSubSV(\&fred);
    $ref = \&fred;
    CallSubSV($ref);
    CallSubSV( sub { print "Hello there\n" } );

ご覧のように call_sv を使えば、Perl サブルーチンをかなり柔軟に 指定することができるようになります。

プログラムの後の部分で使えるように Perl サブルーチンに対応する SV (先の例では name)に格納する必要があるのなら、SV へのポインタの コピーを格納するだけでは十分ではないということに注意すべきです。 先のプログラムが以下のようなものだったとしましょう:

    static SV * rememberSub;

    void
    SaveSub1(name)
        SV *    name
        CODE:
        rememberSub = name;

    void
    CallSavedSub1()
        CODE:
        PUSHMARK(SP);
        call_sv(rememberSub, G_DISCARD|G_NOARGS);

これがまずいという理由は、CallSavedSub1 の中で rememberSub を 使ったときに、SaveSub1 に保持されているPerlサブルーチンをまだ 参照するかもしれない(し、しないかもしれない)ということです。 これは、特に以下のような場合に真となります:

    SaveSub1(\&fred);
    CallSavedSub1();

    SaveSub1( sub { print "Hello there\n" } );
    CallSavedSub1();

上記の SaveSub1 が実行される度に、そのパラメータに対応する SV* は存在しなくなります。 このため、

    Can't use an undefined value as a subroutine reference at ...

Perl が上記のエラーメッセージを出すことが予想されます。

同様に、

    $ref = \&fred;
    SaveSub1($ref);
    $ref = 47;
    CallSavedSub1();

上記のコードを使った場合、これらのメッセージのいずれかが 出力されるでしょう(これは使っているPerlのバージョンによります)。

    Not a CODE reference at ...
    Undefined subroutine &main::47 called ...

変数 $ref は SaveSub1 を呼び出したときにはサブルーチン fred を 参照するように生成されるかもしれませんが、CallSavedSub1 が 呼び出されている時点では、今や数値 47 を保持しているのです。 SaveSub1 にある元々の SV へのポインタしか保存しないので、 $ref に対するなんらかの変更はポインタ rememberSub によって 反映されます。 これは、CallSavedSub1 が呼び出されるときはいつでも、 SV* rememberSub によって参照されるコードを実行することを試みられるという ことです。 しかし上記の例では、rememberSub は整数値 47 を参照していますので、 Perl は不満を漏らすというわけです。

同様の、しかし微妙な問題を以下のプログラム片で説明します:

    $ref = \&fred;
    SaveSub1($ref);
    $ref = \&joe;
    CallSavedSub1();

CallSavedSub1 が呼び出されたときに実行する Perl サブルーチンは SaveSub1 を呼び出したときに要求した fred ではなく、 (それがあるとして)joe なのです。

これらの問題に対処するためには、SV の完全なコピーをとる必要があります。 以下のコードはそれを行った SaveSub2 です。

    static SV * keepSub = (SV*)NULL;

    void
    SaveSub2(name)
        SV *    name
        CODE:
        /* Take a copy of the callback */
        if (keepSub == (SV*)NULL)
            /* First time, so create a new SV */
            keepSub = newSVsv(name);
        else
            /* Been here before, so overwrite */
            SvSetSV(keepSub, name);

    void
    CallSavedSub2()
        CODE:
        PUSHMARK(SP);
        call_sv(keepSub, G_DISCARD|G_NOARGS);

SaveSub2 が呼び出される度に新たなSVを生成してしまうのを防ぐために、 関数はまず最初に自分が以前に呼び出されたことがあるかどうかを 確認します。 もしそれまでに呼び出されたことがなければ、新しい SV のための領域が 割り当てられ、そして Perl サブルーチンへの参照 name が、 newSVsv を使った操作によって変数 keepSub へコピーされます。 その後で SaveSub2 が呼び出されたときは常に、存在するSV keepSubSVSetSV を使って新しい値で上書きされます。

call_argv を使う

次に挙げるのは渡された引数を表示する Perl サブルーチンです。

    sub PrintList
    {
        my(@list) = @_;

        foreach (@list) { print "$_\n" }
    }

そして、PrintList を呼ぶ call_argv の例です。

    static char * words[] = {"alpha", "beta", "gamma", "delta", NULL};

    static void
    call_PrintList()
    {
        dSP;

        call_argv("PrintList", G_DISCARD, words);
    }

注目したいのは、この場合には、PUSHMARK を呼ぶ必要がないということです。 call_argv 側で自動的に行なうからです。

call_method を使う

以下のような Perl コードを考えてみましょう:

    {
        package Mine;

        sub new
        {
            my($type) = shift;
            bless [@_]
        }

        sub Display
        {
            my ($self, $index) = @_;
            print "$index: $$self[$index]\n";
        }

        sub PrintID
        {
            my($class) = @_;
            print "This is Class $class version 1.0\n";
        }
    }

これは配列を管理するための非常に単純なクラスを実装しています。 コンストラクター new を除けば、一つのスタティックメソッドと一つの 仮想メソッドを宣言しています。 スタティックメソッド PrintID は、 単純にクラス名とバージョン番号を出力します。 仮想メソッド Display は配列の要素一つを出力します。 以下にこれを使った例を示します。

    $a = Mine->new('red', 'green', 'blue');
    $a->Display(1);
    Mine->PrintID;

これは次のような出力をします。

    1: green
    This is Class Mine version 1.0

C からの Perl メソッドの呼び出しは実に直接的なものです。 以下に挙げるようなことが要求されます:

以下の例は、C から PrintID, Display の両方のメソッドを呼び出す 仕組みを説明するための簡単なXSUBです。

    void
    call_Method(ref, method, index)
        SV *    ref
        char *  method
        int             index
        CODE:
        PUSHMARK(SP);
        XPUSHs(ref);
        XPUSHs(sv_2mortal(newSViv(index)));
        PUTBACK;

        call_method(method, G_DISCARD);

    void
    call_PrintID(class, method)
        char *  class
        char *  method
        CODE:
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(class, 0)));
        PUTBACK;

        call_method(method, G_DISCARD);

これで PrintID, Display は次のようにして呼び出すことができます:

    $a = Mine->new('red', 'green', 'blue');
    call_Method($a, 'Display', 1);
    call_PrintID('Mine', 'PrintID');

唯一注意すべきことは、スタティックメソッドと仮想メソッドの両方に おいてメソッド名はスタックを通しては渡されない、ということです -- これは call_method の第一引数として使用されます。

GIMME_V を使う

以下は、その時点で実行されているコンテキストを出力するちょっとした XSUB です。

    void
    PrintContext()
        CODE:
        I32 gimme = GIMME_V;
        if (gimme == G_VOID)
            printf ("Context is Void\n");
        else if (gimme == G_SCALAR)
            printf ("Context is Scalar\n");
        else
            printf ("Context is Array\n");

そしてこれをテストするための Perl プログラムです。

    PrintContext;
    $a = PrintContext;
    @a = PrintContext;

出力はこうなります。

    Context is Void
    Context is Scalar
    Context is Array

一時変数の始末に Perl を使う

これまでのところに使った例では、すべての一時変数(call_* 関数に対してスタックを通して渡されるパラメータ、もしくはスタックを 通して関数から返される値)はコールバックの中で生成され、以下に 挙げる手法のいずれかを使って解放されていました:

これとは別の手法があります; それは、コールバックが完了して制御を 取り戻したときにはいつでも Perl に自動的に後始末をさせるというものです。 これは単に以下のような

    ENTER;
    SAVETMPS;
    ...
    FREETMPS;
    LEAVE;

シーケンスをコールバックの中で使うことで行なわれます (もちろん、G_DISCARD フラグは指定しません)。

この手法を使おうとするなら、非常に特定された状況においてメモリリークが 発生する可能性があることを考慮しなければなりません。 その状況を説明するには、Perl とコールバックとの間での制御の流れについて 知っておく必要があります。

本ドキュメントの最初に挙げたサンプル(エラーハンドラとイベント駆動 プログラム)は、コールバックに遭遇するような典型的な制御の流れです。 これら二つの間には非常に重要な違いがありますから、注意しましょう。

最初の例、エラーハンドラでは、制御の流れは以下の様になるでしょう。 外部ライブラリに対するインターフェースは既に作っています。 制御は以下のようにして外部ライブラリに到達できます。

    perl --> XSUB --> external library

制御がライブラリにある間にエラー状況が発生します。 これに対処するためのPerlのコールバックは既に設定していますので、 それがここで実行されます。 コールバックでの処理が完了すれば、制御はPerlの元へと戻ります。 以下はそのような状況における制御の流れです。

    perl --> XSUB --> external library
                      ...
                      error occurs
                      ...
                      external library --> call_* --> perl
                                                          |
    perl <-- XSUB <-- external library <-- call_* <----+

call_* を使ったエラー処理が完了した後で、制御はほぼ即座に Perl へと戻ります。

このダイアグラムにおいて、より正確にはもっと深いスコープになります。 これは、スコープを囲む部分に戻ったり放っておいた一時変数が 解放されるようなダイアグラムの残りの部分に制御が戻ってきたのみです。

二番目の例であるイベント駆動のプログラムでは、制御の流れは以下のように なります。

    perl --> XSUB --> event handler
                      ...
                      event handler --> call_* --> perl
                                                       |
                      event handler <-- call_* <----+
                      ...
                      event handler --> call_* --> perl
                                                       |
                      event handler <-- call_* <----+
                      ...
                      event handler --> call_* --> perl
                                                       |
                      event handler <-- call_* <----+

この場合の制御の以下のような流れは、

    event handler --> call_* --> perl

現実的なプログラムの生存期間を通じてシーケンスの繰り返しからのみ 構成されます。 これはつまり、制御は 決して そのスコープを囲む部分には 戻ってこないということです。

大きな問題っていったいなんでしょうか? そう、Perl があなたのために一時変数を用意するのを期待しているのなら、 そのために長く待たされるかもしれないということです。 Perl が一時変数を破棄するために、制御をその場所を囲んでいるスコープに 戻さなければなりません。 イベント駆動のシナリオにおいてはこれは決して起こりません。 これはつまり、 あなたのプログラムが時がたつにつれて解放されることのない一時変数を どんどん生成するということです。 こういった一時変数は幾らかのメモリを使うので、プログラムもシステムで 使えるメモリのすべてを使いきってしまうかもしれないので -- kapow!

重要な点はこうです -- コールバックの終了後に制御が(コールバックを 囲んでいる)Perl スコープに即座に戻るということを確信しているので あれば、あなたが生成したすべての一時変数を陽に破棄する必要はないのだと いうことです。 気をつけて欲しいのは、すべきことについて良く わからないことがあるのなら、整頓されたものを壊すような真似をしては いけないということです。

コールバックのコンテキスト情報の格納についての戦略

コールバックインターフェースを割り当てるときに現れる最も分かりづらい 潜在的な問題は、C のコールバック関数とそれと等価な Perl ルーチンとの間の マッピングをどのように格納するかということで説明することができます。

これが実際の問題になりうるのを理解するため、まず最初にコールバックが すべての C 環境においてどのようにセットアップされるのかを考えてみましょう。 典型的な C API はコールバックを登録するための関数を提供します。 この関数はその引数の一つとして関数へのポインタを期待します。 以下の例では致命的なエラーが発生したときに呼び出される C 関数を登録する仮想関数 register_fatal を呼び出します。

    register_fatal(cb1);

パラメータ cb1 は関数へのポインタなので、cb1 をプログラム中で 以下の様に定義しておかなければなりません。

    static void
    cb1()
    {
        printf ("Fatal Error\n");
        exit(1);
    }

ここで、register_fatal と等価な Perl ルーチンで Perl サブルーチンを 呼ぶように変更します。

    static SV * callback = (SV*)NULL;

    static void
    cb1()
    {
        dSP;

        PUSHMARK(SP);

        /* Call the Perl sub to process the callback */
        call_sv(callback, G_DISCARD);
    }

    void
    register_fatal(fn)
        SV *    fn
        CODE:
        /* Remember the Perl sub */
        if (callback == (SV*)NULL)
            callback = newSVsv(fn);
        else
            SvSetSV(callback, fn);

        /* register the callback with the external library */
        register_fatal(cb1);

コールバック pcb1 を以下の様に登録します。

    # Register the sub pcb1
    register_fatal(\&pcb1);

    sub pcb1
    {
        die "I'm dying...\n";
    }

C のコールバックと Perl との間のマッピングをするために、グローバル変数 callback に格納されます。

これは単に一つのコールバックを好きなときに登録できればよい、というのなら 十分でしょう。 その一つの例は、上で説明したエラーハンドラのようなものです。 しかし忘れないで欲しいのは、register_fatal を繰り返し呼び出すことで、 以前に登録していたコールバック関数を新しいもので 置き換えてしまうということです。

非同期ファイル入出力のライブラリに対するインターフェースが 欲しいとしましょう。 この場合、読み込み操作が終了したときに常にコールバックされるものを 登録することができるでしょう。 私たちは何かをするために、分割された Perl サブルーチンを オープンされているファイル毎に呼び出すことができます。 現状では、一度に一つのコールバックしか許していないために上の例に あるようなエラーハンドラは十分なものではありません。 私たちが必要としているのは、オープンされたファイルとそのファイルに 対応して呼び出したい Perl サブルーチンとの間のマッピングを 格納するということです。

ファイルハンドル fh を伴った ProcessRead という C の関数に 結び付けられた asynch_read を持った入出力ライブラリを 考えてみましょう -- これは、その関数がファイルをオープンし、 ファイルハンドルを取得するいくつかのルーチンを提供していることを 仮定しています。

    asynch_read(fh, ProcessRead)

これは以下のような形式の C の関数 ProcessRead を期待しているでしょう。

    void
    ProcessRead(fh, buffer)
    int fh;
    char *      buffer;
    {
         ...
    }

このライブラリに対する Perl インターフェースを提供するためには、 パラメータ fh と、呼び出しをしたい Perl サブルーチンとの間の マップが可能であることが必要です。 ハッシュはこのマッピングを格納するために便利な機構です。 以下のコードは可能な実装の一例です。

    static HV * Mapping = (HV*)NULL;

    void
    asynch_read(fh, callback)
        int     fh
        SV *    callback
        CODE:
        /* If the hash doesn't already exist, create it */
        if (Mapping == (HV*)NULL)
            Mapping = newHV();

        /* Save the fh -> callback mapping */
        hv_store(Mapping, (char*)&fh, sizeof(fh), newSVsv(callback), 0);

        /* Register with the C Library */
        asynch_read(fh, asynch_read_if);

そして、asynch_read_if は以下のようになるでしょう。

    static void
    asynch_read_if(fh, buffer)
    int fh;
    char *      buffer;
    {
        dSP;
        SV ** sv;

        /* Get the callback associated with fh */
        sv =  hv_fetch(Mapping, (char*)&fh , sizeof(fh), FALSE);
        if (sv == (SV**)NULL)
            croak("Internal error...\n");

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSViv(fh)));
        XPUSHs(sv_2mortal(newSVpv(buffer, 0)));
        PUTBACK;

        /* Call the Perl sub */
        call_sv(*sv, G_DISCARD);
    }

完全のため、asynch_close も例示します。 これはハッシュ Mapping からエントリーを取り除く方法を示すものです。

    void
    asynch_close(fh)
        int     fh
        CODE:
        /* Remove the entry from the hash */
        (void) hv_delete(Mapping, (char*)&fh, sizeof(fh), G_DISCARD);

        /* Now call the real asynch_close */
        asynch_close(fh);

このため、Perl のインターフェースは次のようになります。

    sub callback1
    {
        my($handle, $buffer) = @_;
    }

    # Register the Perl callback
    asynch_read($fh, \&callback1);

    asynch_close($fh);

C のコールバックと Perl との間のマッピングは、このときに Mapping という グローバルハッシュに格納されます。 ハッシュを使うことによって、登録できるコールバックの数に 制限がなくなります。

ファイルハンドルと Perl サブルーチンとのマッピングを許しているパラメータを 含んでいないコールバックによって提供されている インターフェースだとするとどうでしょう? 非同期の I/O パッケージを考えてみましょう; コールバック関数は以下のように、 buffer パラメータのみを取ります。

    void
    ProcessRead(buffer)
    char *      buffer;
    {
        ...
    }

ファイルハンドルなしに、直接 C のコールバック関数を Perl のサブルーチンに マップする方法はありません。

このような場合に可能な方法は、Perl に対するインターフェースのように C 関数の並びをあらかじめ定義してしまうことです; したがって、

    #define MAX_CB              3
    #define NULL_HANDLE -1
    typedef void (*FnMap)();

    struct MapStruct {
        FnMap    Function;
        SV *     PerlSub;
        int      Handle;
      };

    static void  fn1();
    static void  fn2();
    static void  fn3();

    static struct MapStruct Map [MAX_CB] =
        {
            { fn1, NULL, NULL_HANDLE },
            { fn2, NULL, NULL_HANDLE },
            { fn3, NULL, NULL_HANDLE }
        };

    static void
    Pcb(index, buffer)
    int index;
    char * buffer;
    {
        dSP;

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSVpv(buffer, 0)));
        PUTBACK;

        /* Call the Perl sub */
        call_sv(Map[index].PerlSub, G_DISCARD);
    }

    static void
    fn1(buffer)
    char * buffer;
    {
        Pcb(0, buffer);
    }

    static void
    fn2(buffer)
    char * buffer;
    {
        Pcb(1, buffer);
    }

    static void
    fn3(buffer)
    char * buffer;
    {
        Pcb(2, buffer);
    }

    void
    array_asynch_read(fh, callback)
        int             fh
        SV *    callback
        CODE:
        int index;
        int null_index = MAX_CB;

        /* Find the same handle or an empty entry */
        for (index = 0; index < MAX_CB; ++index)
        {
            if (Map[index].Handle == fh)
                break;

            if (Map[index].Handle == NULL_HANDLE)
                null_index = index;
        }

        if (index == MAX_CB && null_index == MAX_CB)
            croak ("Too many callback functions registered\n");

        if (index == MAX_CB)
            index = null_index;

        /* Save the file handle */
        Map[index].Handle = fh;

        /* Remember the Perl sub */
        if (Map[index].PerlSub == (SV*)NULL)
            Map[index].PerlSub = newSVsv(callback);
        else
            SvSetSV(Map[index].PerlSub, callback);

        asynch_read(fh, Map[index].Function);

    void
    array_asynch_close(fh)
        int     fh
        CODE:
        int index;

        /* Find the file handle */
        for (index = 0; index < MAX_CB; ++ index)
            if (Map[index].Handle == fh)
                break;

        if (index == MAX_CB)
            croak ("could not close fh %d\n", fh);

        Map[index].Handle = NULL_HANDLE;
        SvREFCNT_dec(Map[index].PerlSub);
        Map[index].PerlSub = (SV*)NULL;

        asynch_close(fh);

この場合、fn1, fn2, fn3 といった関数は呼び出される Perlサブルーチンを記憶するために使われます。 それぞれの関数は関数 Pcb 中で配列 Map にアクセスするためと、 実際に Perl サブルーチンを呼び出すための独立した hard-wired な添え字を 保持しています。

この技法に関して、幾つかの明らかな短所があります。

第一に、前の例に比べるとプログラムがより複雑になっているということ。

第二に、同時に存在できるコールバックの数について作り付け(hard-wired)の 限界があるということです(先の例では3)。 この限界を大きくする唯一の方法は、プログラムそのものを変更して より多くの関数を使えるようにし、さらに再コンパイルするというものです。 それでもなお、関数の数を注意深く選択する限りにおいては、この方法は 実用的な解決策であり、また、一部の状況においては唯一の選択肢なのです。

まとめとして、C と Perl コールバックとの間のマッピングを格納するために 使うことのできる手法を以下に挙げました。

問題を無視する - ただ一つのコールバックだけを許す

多くの状況で、エラーハンドラへのインターフェースと同じく、 これは十分適切な解決策となるでしょう。

コールバックの並びを作成する - 作り付けの限界

C のコールバックから渡されたパラメータを区別することができなければ、 C のコールバックインターフェース関数の並びを生成して、 配列にポインタを格納する必要があるでしょう。

Perlのコールバックへマップするためのパラメータを使う

ハッシュは C と Perl との間のマッピングを格納するのに 最適なメカニズムです。

代替スタックの操作

POP* マクロを、Perl サブルーチンからの戻り値にアクセスすることのみに 使用するようにしたにも関らず、これらのマクロをバイパスして ST マクロ(ST マクロに関する記述は perlxs を参照してください)を 使ってスタックを読み取ることが可能です。

POP* マクロが適切であるべき場合のほとんどにおける主な問題は、 それらのマクロが戻り値をシーケンシャルに処理することを 強制するということです。 これは一部のケースにおいては、値を処理する方法として最も適切な 方法ではありません。 私たちが欲しているのは、スタックに対してのアクセスをランダムな 順序で行えるということです。 ST マクロは、XSUB をコーディングする際にこの目的のために 適切であるときに使われます。

以下に挙げたプログラム片は、セクション "Returning a List of Values" に あった例で、POP* の代わりに ST を記録のために使っています。

    static void
    call_AddSubtract2(a, b)
    int a;
    int b;
    {
        dSP;
        I32 ax;
        int count;

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSViv(a)));
        XPUSHs(sv_2mortal(newSViv(b)));
        PUTBACK;

        count = call_pv("AddSubtract", G_ARRAY);

        SPAGAIN;
        SP -= count;
        ax = (SP - PL_stack_base) + 1;

        if (count != 2)
            croak("Big trouble\n");

        printf ("%d + %d = %d\n", a, b, SvIV(ST(0)));
        printf ("%d - %d = %d\n", a, b, SvIV(ST(1)));

        PUTBACK;
        FREETMPS;
        LEAVE;
    }

注意点

  1. ax という変数を定義する必要があったことに注意してください。 これは、ST マクロがその変数が存在することを期待しているからです。 XSUB の中でなければ、(すでにあるかのようにみなしてしまって)ax を 定義する必要はなかったでしょう。

  2. このようなコードは

            SPAGAIN;
            SP -= count;
            ax = (SP - PL_stack_base) + 1;

    スタックのセットアップを行うので、ST マクロを使うことができます。

  3. この例のオリジナルのコーディングとは異なり、戻り値は逆順で アクセスされることはありません。 このため、ST(0) は Perl サブルーチンの戻り値の最初の 要素を参照し、ST(count-1) は最後の要素を参照します。

C の中で無名サブルーチンの作成と呼び出しを行う

既に述べてきたように、call_sv は無名サブルーチンを起動するために 使うことができます。 しかしながら、一つの例が Perl スクリプトがどのように XSUB を起動して この操作を行うかを示しました。 C プログラムの内側でどのようにするかをお見せしましょう。

 ...

 SV *cvrv = eval_pv("sub { print 'You will not find me cluttering any namespace!' }", TRUE);

 ...

 call_sv(cvrv, G_VOID|G_NOARGS);

eval_pv は無名サブルーチンをコンパイルするために 使われ、この関数は値を返します (eval_pv の詳細は "eval_pv" in perlapi を参照してください)。 このコードリファレンスが作用してしまえば、先に挙げた例と 組み合わせることができます。

軽量コールバック

同じサブルーチンを繰り返し起動する必要がある場合もあります。 これは普通、Perl 組み込みの sort() のように、値のリストに対して動作する 関数で起こります。 sort() に比較関数を渡せますが、これは比較が必要な値の組毎に起動されます。 List::Util の first() 関数と reduce() 関数は同様なパターンに従います。

この場合、軽量コールバック API を使ってルーチンを(しばしばかなり大幅に) 高速化できます。 考え方としては、呼び出しコンテキストは作成時と破壊時に 1 回ずつしか 必要ではなく、サブルーチンはその間に任意の関数呼び出すことができます。

グローバル変数を使ってパラメータを渡すのはよくあることです (典型的には 1 パラメータ用には $_ を、あるいは 2 パラメータのためには $a と $b を) @_ よりも使われます。 (もし何をしようとしているのかが分かっているなら @_ 機構を使うことも できますが、これのための支援 API はまだありません。 これはまた、本質的に低速です。)

マクロ呼び出しのパターンは以下のようなものです:

    dMULTICALL;                 /* Declare local variables */
    I32 gimme = G_SCALAR;       /* context of the call: G_SCALAR,
                                 * G_ARRAY, or G_VOID */

    PUSH_MULTICALL(cv);         /* Set up the context for calling cv,
                                   and set local vars appropriately */

    /* loop */ {
        /* set the value(s) af your parameter variables */
        MULTICALL;              /* Make the actual call */
    } /* end of loop */

    POP_MULTICALL;              /* Tear down the calling context */

具体的な例としては、List::Util 1.18 の first() 関数と reduce() 関数の実装を 参照してください。 また、より古いバージョンの perl のための多重呼び出し API をエミュレートする ヘッダファイルもあります。

SEE ALSO

perlxs, perlguts, perlembed

AUTHOR

Paul Marquess

Special thanks to the following people who assisted in the creation of the document.

Jeff Okamoto, Tim Bunce, Nick Gianniotis, Steve Kelem, Gurusamy Sarathy and Larry Wall.

DATE

Version 1.3, 14th Apr 1997