#
1.6 C から Python 関数を呼び出す

これまでは、Python からの C 関数の呼び出しに重点を置いて 述べてきました。ところでこの逆: C からの Python 関数の呼び出し もまた有用です。 とりわけ、いわゆる ``コールバック'' 関数をサポートするような ライブラリを作成する際にはこの機能が便利です。 ある C インタフェースがコールバックを利用している場合、 同等の機能を提供する Python コードでは、しばしば Python プログラマに コールバック機構を提供する必要があります; このとき実装では、 C で書かれたコールバック関数から Python で書かれたコールパック関数 を呼び出すようにする必要があるでしょう。 もちろん、他の用途も考えられます。

幸運なことに、Python インタプリタは簡単に再帰呼び出しでき、 Python 関数を呼び出すための標準インタフェースもあります。 (Python パーザを特定の入力文字を使って呼び出す方法について 詳説するつもりはありません -- この方法に興味があるなら、 Python ソースコードの Python/pythonmain.c にある、 コマンドラインオプション-c の実装を見てください)

Python 関数の呼び出しは簡単です。まず、C のコードに対して コールバックを登録しようとする Python プログラムは、何らかの方法で Python の関数オブジェクトを渡さねばなりません。このために、 コールバック登録関数 (またはその他のインタフェース) を提供 せねばなりません。このコールバック登録関数が呼び出された際に、 引き渡された Python 関数オブジェクトへのポインタをグローバル変数に -- あるいは、どこか適切な場所に -- 保存します (関数オブジェクトを`Py_INCREF()` するようよく注意して ください!)。例えば、以下のような関数がモジュールの一部になって いることでしょう:

    static PyObject *my_callback = NULL;

    static PyObject *
    my_set_callback(PyObject *dummy, PyObject *args)
    {
        PyObject *result = NULL;
        PyObject *temp;

        if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
            if (!PyCallable_Check(temp)) {
                PyErr_SetString(PyExc_TypeError, "parameter must be callable");
                return NULL;
            }
            Py_XINCREF(temp);         /* 新たなコールバックへの参照を追加 */
            Py_XDECREF(my_callback);  /* 以前のコールバックを捨てる */
            my_callback = temp;       /* 新たなコールバックを記憶 */
            /* "None" を返す際の定型句 */
            Py_INCREF(Py_None);
            result = Py_None;
        }
        return result;
    }

この関数は`METH_VARARGS` フラグを使ってインタプリタに 登録せねばなりません; `METH_VARARGS` フラグについては、 1.4 節、 ``モジュールのメソッドテーブルと初期化関数'' で説明しています。 `PyArg_ParseTuple()` 関数とその引数については、 1.7 節、 ``拡張モジュール関数でのパラメタ展開'' に記述しています。

`Py_XINCREF()` および`Py_XDECREF()` は、 オブジェクトに対する参照カウントをインクリメント/デクリメントする ためのマクロで、`NULL` ポインタが渡されても安全に操作できる 形式です (とはいえ、上の流れではtemp が`NULL` になることは ありません)。 これらのマクロと参照カウントについては、1.10 節、 ``参照カウント'' で説明しています。

その後、コールバック関数を呼び出す時が来たら、C 関数 `PyEval_CallObject()` を呼び出します。 この関数には二つの引数: Python 関数と Python 関数の引数リストがあり、 いずれも任意の Python オブジェクトを表すポインタ型です。 引数リストは常にタプルオブジェクトでなければならず、その長さは 引数の数になります。Python 関数を引数なしで呼び出すのなら 空のタプルを渡します; 単一の引数で関数を呼び出すのなら、 単要素 (singleton) のタプルを渡します。 `Py_BuildValue()` の書式化文字列中に、ゼロ個または 一個以上の書式化コードが入った丸括弧がある場合、この関数は タプルを返します。以下に例を示します:

        int arg;
        PyObject *arglist;
        PyObject *result;
        ...
        arg = 123;
        ...
        /* ここでコールバックを呼ぶ */
        arglist = Py_BuildValue("(i)", arg);
        result = PyEval_CallObject(my_callback, arglist);
        Py_DECREF(arglist);

`PyEval_CallObject()` は Python オブジェクトへのポインタを 返します; これは Python 関数からの戻り値になります。 `PyEval_CallObject()` は、引数に対して ``参照カウント中立 (reference-count-neutral)'' です。 上の例ではタプルを生成して引数リストとして提供しており、この タプルは呼び出し直後に `Py_DECREF()` しています。

`PyEval_CallObject()` は ``新しい'' 戻り値を返します: 戻り値が表すオブジェクトは新たなオブジェクトか、既存のオブジェクトの 参照カウントをインクリメントしたものです。従って、このオブジェクトを グローバル変数に保存したいのでないかぎり、 たとえこの戻り値に興味がなくても (むしろ、そうであればなおさら!) 何がしかの方法で戻り値オブジェクトを `Py_DECREF()` しなければなりません。

とはいえ、戻り値を`Py_DECREF()` する前には、値が `NULL` でないかチェックしておくことが重要です。もし `NULL`なら、呼び出した Python 関数は例外を送出して終了させられています。 `PyEval_CallObject()` を呼び出しているコード自体もまた Python から呼び出されているのであれば、今度は C コードが自分を 呼び出している Python コードにエラー標示値を返さねばなりません。 それにより、インタプリタはスタックトレースを出力したり、例外を 処理するための Python コードを呼び出したりできます。 例外の送出が不可能だったり、したくないのなら、 `PyErr_Clear()` を呼んで例外を消去しておかねばなりません。 例えば以下のようにします:

        if (result == NULL)
            return NULL; /* エラーを返す */
        ...use result...
        Py_DECREF(result);

Python コールバック関数をどんなインタフェースにしたいかによっては、 引数リストを`PyEval_CallObject()` に与えなければ ならない場合もあります。 あるケースでは、コールバック関数を指定したのと同じインタフェース を介して、引数リストも渡されているかもしれません。 また別のケースでは、新しいタプルを構築して引数リストを渡さねば ならないかもしれません。この場合最も簡単なのは `Py_BuildValue()` を呼ぶやり方です。 例えば、整数のイベントコードを渡したければ、以下のようなコードを 使うことになるでしょう:

        PyObject *arglist;
        ...
        arglist = Py_BuildValue("(l)", eventcode);
        result = PyEval_CallObject(my_callback, arglist);
        Py_DECREF(arglist);
        if (result == NULL)
            return NULL; /* エラーを返す */
        /* 場合によってはここで結果を使うかもね */
        Py_DECREF(result);

"`Py_DECREF(arglist)`" が呼び出しの直後、エラーチェックよりも前に 置かれていることに注意してください! また、厳密に言えば、このコードは 完全ではありません: `Py_BuildValue()` はメモリ不足に おちいるかもしれず、チェックしておくべきです。