さて、前に約束したように、spam_system() Python プログラム からどうやって呼び出すかをこれから示します。まずは、関数名とアドレスを ``メソッドテーブル (method table)'' に列挙する必要があります:
static PyMethodDef SpamMethods[] = { ... {"system", spam_system, METH_VARARGS, "Execute a shell command."}, ... {NULL, NULL, 0, NULL} /* Sentinel */ };
リスト要素の三つ目のエントリ ("METH_VARARGS") に注意してください。
このエントリは、C 関数が使う呼び出し規約をインタプリタに教えるための
フラグです。通常この値は"METH_VARARGS" か
"METH_VARARGS | METH_KEYWORDS" のはずです; 0
は旧式のPyArg_ParseTuple() の変化形が使われることを
意味します。
"METH_VARARGS" だけを使う場合、C 関数は、Python レベルでの引数が PyArg_ParseTuple() が受理できるタプルの形式で渡されるもの と想定しなければなりません; この関数についての詳細は下で説明します。
関数にキーワード引数が渡されることになっているのなら、 第三フィールドにMETH_KEYWORDS ビットをセットできます。 この場合、C 関数は第三引数に "PyObject *" を受理するように せねばなりません。このオブジェクトは、キーワード引数の辞書に なります。こうした関数で引数を解釈するには、 PyArg_ParseTupleAndKeywords() を使ってください。
メソッドテーブルは、モジュールの初期化関数内でインタプリタに 渡さねばなりません。初期化関数はモジュールの名前を name としたときに initname() という名前でなければ ならず、モジュールファイル内で定義されているもののうち、唯一の 非static 要素でなければなりません:
PyMODINIT_FUNC initspam(void) { (void) Py_InitModule("spam", SpamMethods); }
PyMODINIT_FUNC は関数の戻り値を void
になるように宣言し、
プラットフォーム毎に必要とされる、特有のリンク宣言 (linkage declaration)
を定義すること、さらに C++ の場合には関数を extern "C"
に
宣言することに注意してください。
Python プログラムがモジュール spam を初めて import
するとき、initspam() が呼び出されます。
(Python の埋め込みに関するコメントは下記を参照してください。)
initspam() は Py_InitModule() を呼び出して
``モジュールオブジェクト'' を生成し (オブジェクトは"spam"
を
キーとして辞書 sys.modules
に挿入されます)、第二引数として
与えたメソッドテーブル (PyMethodDef 構造体の配列) の情報に
基づいて、組み込み関数オブジェクトを新たなモジュールに挿入していきます。
Py_InitModule() は、自らが生成した (この段階ではまだ未使用の)
モジュールオブジェクトへのポインタを返します。
Py_InitModule() は、モジュールを満足に初期化できなかった場合、
致命的エラーで中断するため、この関数の呼び出し側がエラーをチェックする
必要はありません。
Python を埋め込む場合には、_PyImport_Inittab テーブルの エントリ内に initspam() がない限り、initspam() は自動的には呼び出されません。この問題を解決する最も簡単な方法は、 Py_Initialize() や PyMac_Initialize() を 呼び出した後に initspam() を直接呼び出し、 静的にリンクしておいたモジュールを静的に初期化してしまうというものです:
int main(int argc, char *argv[]) { /* Python インタプリタに argv[0] を渡す */ Py_SetProgramName(argv[0]); /* Python インタプリタを初期化する。必ず必要。 */ Py_Initialize(); /* 静的モジュールを追加する */ initspam();
Python ソース配布物中の Demo/embed/demo.c ファイル内 に例があります。
注意:
単一のプロセス内 (または
fork() 後の exec() が介入していない状態)
における複数のインタプリタにおいて、 sys.module
から
エントリを除去したり新たなコンパイル済みモジュールを import
したりすると、拡張モジュールによっては問題を生じることがあります。
拡張モジュールの作者は、内部データ構造を初期化する際にはよくよく
用心すべきです。また、reload() 関数を拡張モジュールに
対して利用でき、この場合はモジュール初期化関数 (initspam())
は呼び出されますが、モジュールが動的にロード可能なオブジェクトファイル
(Unixでは .so、Windows では .dll) から読み出された
場合にはモジュールファイルを再読み込みしないので注意してください。
より実質的なモジュール例は、Python ソース配布物に Modules/xxmodule.c という名前で入っています。 このファイルはテンプレートとしても利用できますし、単に例としても 読めます。ソース配布物や Windows にインストールされた Python に入っている modulator.py では、拡張モジュールで実装しなければならない 関数やオブジェクトを宣言し、実装部分を埋めて作成するためのテンプレート を生成できるような、簡単なグラフィカルユーザインタフェースを 提供しています。 このスクリプトはTools/modulator/ ディレクトリにあります; 詳しくはディレクトリ内の README ファイルを参照してください。
ご意見やご指摘をお寄せになりたい方は、 このドキュメントについて... をご覧ください。