1. C や C++ による Python の拡張¶
C プログラムの書き方を知っているなら、Python に新たな組み込みモジュールを追加するのはきわめて簡単です。この新たなモジュール、拡張モジュール (extension module) を使うと、Python が直接行えない二つのこと: 新しい組み込みオブジェクトの実装、そして全ての C ライブラリ関数とシステムコールに対する呼び出し、ができるようになります。
拡張モジュールをサポートするため、Python API (Application Programmer's Interface) では一連の関数、マクロおよび変数を提供していて、Python ランタイムシステムのほとんどの側面へのアクセス手段を提供しています。 Python API は、ヘッダ "Python.h" をインクルードして C ソースに取り込みます。
拡張モジュールのコンパイル方法は、モジュールの用途やシステムの設定方法に依存します。詳細は後の章で説明します。
注釈
C 拡張のインターフェイスは CPython に固有のものであり、これによる拡張モジュールはほかの Python 実装では動作しません。多くの場合、C 拡張を書くことを避けてほかの Python 実装のために移植性を確保することは可能です。たとえば、あなたがしたいことが C ライブラリの関数やシステムコールを呼び出すことである場合、 ctypes あるいは cffi ライブラリの利用を検討すべきです。これらのモジュールは C コードとインターフェイスし、C 拡張を書いてコンパイルするのに較べて Python 実装間のより高い移植性をもった Python コードを書かせてくれます。
1.1. 簡単な例¶
spam (Monty Python ファンの好物ですね) という名の拡張モジュールを作成することにして、C ライブラリ関数 system() に対する Python インターフェイスを作成したいとします。 [1] この関数は null で終端されたキャラクタ文字列を引数にとり、整数を返します。この関数を以下のようにして Python から呼び出せるようにしたいとします。
>>> import spam
>>> status = spam.system("ls -l")
まずは spammodule.c を作成するところから始めます。 (伝統として、 spam という名前のモジュールを作成する場合、モジュールの実装が入った C ファイルを spammodule.c と呼ぶことになっています; spammify のように長すぎるモジュール名の場合には、単に spammify.c にもできます。)
このファイルの最初の 2 行は以下のようにします:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
これで、Python API を取り込みます (必要なら、モジュールの用途に関する説明や、著作権表示を追加します)。
注釈
Python は、システムによっては標準ヘッダの定義に影響するようなプリプロセッサ定義を行っているので、 Python.h をいずれの標準ヘッダよりも前にインクルード せねばなりません 。
#define PY_SSIZE_T_CLEAN は、いくつかの API では int の代わりに Py_ssize_t が使われるべきであることを示すために使われていました。これは Python 3.13 以降では不要となりましたが、後方互換性のために維持されています。このマクロの説明は 文字列とバッファ にあります。
All user-visible symbols defined by Python.h have a prefix of Py or
PY, except those defined in standard header files.
Tip
For backward compatibility, Python.h includes several standard header files.
C extensions should include the standard headers that they use,
and should not rely on these implicit includes.
If using the limited C API version 3.13 or newer, the implicit includes are:
<assert.h><intrin.h>(on Windows)<inttypes.h><limits.h><math.h><stdarg.h><wchar.h><sys/types.h>(if present)
If Py_LIMITED_API is not defined, or is set to version 3.12 or older,
the headers below are also included:
<ctype.h><unistd.h>(on POSIX)
If Py_LIMITED_API is not defined, or is set to version 3.10 or older,
the headers below are also included:
<errno.h><stdio.h><stdlib.h><string.h>
次にファイルに追加する内容は、Python 式 spam.system(string) を評価する際に呼び出されることになる C 関数です (この関数を最終的にどのように呼び出すかは、後ですぐわかります):
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
return PyLong_FromLong(sts);
}
ここでは、Python の引数リスト (例えば、単一の式 "ls -l") から C 関数に渡す引数にそのまま変換しています。 C 関数は常に二つの引数を持ち、便宜的に self および args と呼ばれます。
self 引数には、モジュールレベルの関数であればモジュールが、メソッドにはオブジェクトインスタンスが渡されます。
args 引数は、引数の入った Python タプルオブジェクトへのポインタになります。タプル内の各要素は、呼び出しの際の引数リストにおける各引数に対応します。引数は Python オブジェクトです --- C 関数で引数を使って何かを行うには、オブジェクトから C の値に変換せねばなりません。Python API の関数 PyArg_ParseTuple() は引数の型をチェックし、C の値に変換します。 PyArg_ParseTuple() はテンプレート文字列を使って、引数オブジェクトの型と、変換された値を入れる C 変数の型を判別します。これについては後で詳しく説明します。
PyArg_ParseTuple() は、全ての引数が正しい型を持っていて、アドレス渡しされた各変数に各引数要素を保存したときに真 (非ゼロ) を返します。この関数は不正な引数リストを渡すと偽 (ゼロ) を返します。後者の場合、関数は適切な例外を送出するので、呼び出し側は (例にもあるように) すぐに NULL を返すようにしてください。
1.2. 幕間小話: エラーと例外¶
An important convention throughout the Python interpreter is the following: when
a function fails, it should set an exception condition and return an error value
(usually -1 or a NULL pointer). Exception information is stored in
three members of the interpreter's thread state. These are NULL if
there is no exception. Otherwise they are the C equivalents of the members
of the Python tuple returned by sys.exc_info(). These are the
exception type, exception instance, and a traceback object. It is important
to know about them to understand how errors are passed around.
Python API では、様々な型の例外をセットするための関数をいくつか定義しています。
もっともよく用いられるのは PyErr_SetString() です。引数は例外オブジェクトと C 文字列です。例外オブジェクトは通常、 PyExc_ZeroDivisionError のような定義済みのオブジェクトです。 C 文字列はエラーの原因を示し、Python 文字列オブジェクトに変換されて例外の "付属値" に保存されます。
もう一つ有用な関数として PyErr_SetFromErrno() があります。この関数は引数に例外だけをとり、付属値はグローバル変数 errno から構築します。もっとも汎用的な関数は PyErr_SetObject() で、二つのオブジェクト、例外と付属値を引数にとります。これら関数に渡すオブジェクトには Py_INCREF() を使う必要はありません。
例外がセットされているかどうかは、 PyErr_Occurred() を使って非破壊的に調べられます。この関数は現在の例外オブジェクトを返します。例外が発生していない場合には NULL を返します。通常は、関数の戻り値からエラーが発生したかを判別できるはずなので、 PyErr_Occurred() を呼び出す必要はありません。
別の関数 g を呼び出した関数 f が、その呼び出しの失敗を検出した場合、f 自身もエラー値 (通常は NULL または -1) を返す必要があります。
このとき PyErr_* 関数を呼び出しては いけません。
g がすでに呼び出しているからです。
次に f の呼び出し元も、PyErr_* を 呼び出さずに、その呼び出し元 へエラーを示す値を返します。
以下同様に続きます。
エラーの最も詳しい原因は、最初にエラーを検出した関数がすでに報告しています。
エラーが Python インタープリタのメインループに到達すると、実行中の Python コードの処理を中断し、Python プログラマーが指定した例外ハンドラーを探します。
(別の PyErr_* 関数を呼び出すことで、モジュールがより詳しいエラーメッセージを提供できる場合は、そうしても構いません。
ただし、一般には不要であり、エラーの原因に関する情報が失われるおそれがあります。
ほとんどの操作は様々な理由で失敗し得るためです。)
ある関数呼び出しでの処理の失敗によってセットされた例外を無視するには、 PyErr_Clear() を呼び出して例外状態を明示的に消去しなくてはなりません。エラーをインタプリタには渡したくなく、自前で (何か他の作業を行ったり、何も起こらなかったかのように見せかけるような) エラー処理を完全に行う場合にのみ、 PyErr_Clear() を呼び出すようにすべきです。
malloc() の呼び出し失敗は、常に例外にしなくてはなりません --- malloc() (または realloc()) を直接呼び出しているコードは、 PyErr_NoMemory() を呼び出して、失敗を示す値を返さねばなりません。オブジェクトを生成する全ての関数 (例えば PyLong_FromLong()) は PyErr_NoMemory() の呼び出しを済ませてしまうので、この規則が関係するのは直接 malloc() を呼び出すコードだけです。
また、PyArg_ParseTuple() などの重要な例外を除き、整数の状態コードを返す関数は通常、Unix のシステムコールと同じく、成功時に正の値またはゼロを返し、失敗時に -1 を返します。
最後に、エラー標示値を返す際に、(エラーが発生するまでに既に生成してしまったオブジェクトに対して Py_XDECREF() や Py_DECREF() を呼び出して) ごみ処理を注意深く行ってください!
どの例外を送出するかは自由に選べます。
PyExc_ZeroDivisionError など、Python のすべての組み込み例外に対応する C オブジェクトが宣言済みで、そのまま利用できます。
もちろん、例外は適切に選ぶ必要があります。
例えば、ファイルを開けなかったことを示すために PyExc_TypeError を使うべきではありません (おそらく PyExc_OSError が適切です)。
引数リストに問題がある場合、PyArg_ParseTuple() 関数は通常 PyExc_TypeError を送出します。
引数の値が特定の範囲内でなければならない場合や、その他の条件を満たす必要がある場合は、PyExc_ValueError が適切です。
You can also define a new exception that is unique to your module. The simplest way to do this is to declare a static global object variable at the beginning of the file:
static PyObject *SpamError = NULL;
and initialize it by calling PyErr_NewException() in the module's
Py_mod_exec function (spam_module_exec()):
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Since SpamError is a global variable, it will be overwritten every time
the module is reinitialized, when the Py_mod_exec function is called.
For now, let's avoid the issue: we will block repeated initialization by raising an
ImportError:
static PyObject *SpamError = NULL;
static int
spam_module_exec(PyObject *m)
{
if (SpamError != NULL) {
PyErr_SetString(PyExc_ImportError,
"cannot initialize spam module more than once");
return -1;
}
SpamError = PyErr_NewException("spam.error", NULL, NULL);
if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot spam_module_slots[] = {
{Py_mod_exec, spam_module_exec},
{0, NULL}
};
static struct PyModuleDef spam_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "spam",
.m_size = 0, // non-negative
.m_slots = spam_module_slots,
};
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
例外オブジェクトの Python での名前は spam.error であることに注意してください。
PyErr_NewException() 関数は、NULL の代わりに別のクラスを渡さなければ、Built-in Exceptions で説明する Exception を基底クラスとするクラスを作成できます。
SpamError 変数が新しく作成した例外クラスへの参照を保持している点にも注意してください。
これは意図的なものです。
外部のコードがモジュールから例外を削除する可能性があるため、クラスへの参照を所有しておく必要があります。
そうすることで、クラスが破棄されて SpamError がダングリングポインターになるのを防ぎます。
ダングリングポインターになると、その例外を送出する C コードがコアダンプなどの意図しない副作用を引き起こすおそれがあります。
For now, the Py_DECREF() call to remove this reference is missing.
Even when the Python interpreter shuts down, the global SpamError
variable will not be garbage-collected. It will "leak".
We did, however, ensure that this will happen at most once per process.
We discuss the use of PyMODINIT_FUNC as a function return type later in this
sample.
次のように PyErr_SetString() を呼び出すと、拡張モジュールから spam.error 例外を送出できます。
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
}
1.3. 例に戻る¶
先ほどの関数の例に戻ると、今度は以下の実行文を理解できるはずです:
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
It returns NULL (the error indicator for functions returning object pointers)
if an error is detected in the argument list, relying on the exception set by
PyArg_ParseTuple(). Otherwise the string value of the argument has been
copied to the local variable command. This is a pointer assignment and
you are not supposed to modify the string to which it points (so in Standard C,
the variable command should properly be declared as const char
*command).
次の文では、 PyArg_ParseTuple() で得た文字列を渡して Unix 関数 system() を呼び出しています:
sts = system(command);
Our spam.system() function must return the value of sts as a
Python object. This is done using the function PyLong_FromLong().
return PyLong_FromLong(sts);
上の場合では、整数オブジェクトを返します。(そう、整数ですら、 Python においてはヒープ上のオブジェクトなのです! )
何ら有用な値を返さない関数 (void を返す関数) に対応する Python の関数は None を返さねばなりません。関数に None を返させるには、以下のような慣用句を使います (この慣用句は Py_RETURN_NONE マクロに実装されています):
Py_INCREF(Py_None);
return Py_None;
Py_None は特殊な Pyhton オブジェクトである None に対応する C での名前です。これまで見てきたようにほとんどのコンテキストで "エラー" を意味する NULL ポインタとは違い、 None は純粋な Python のオブジェクトです。
1.4. モジュールのメソッドテーブルと初期化関数¶
I promised to show how spam_system() is called from Python programs.
First, we need to list its name and address in a "method table":
static PyMethodDef spam_methods[] = {
...
{"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() が受理できるタプルの形式で渡されるものと想定しなければなりません; この関数についての詳細は下で説明します。
The METH_KEYWORDS bit may be set in the third field if keyword
arguments should be passed to the function. In this case, the C function should
accept a third PyObject * parameter which will be a dictionary of keywords.
Use PyArg_ParseTupleAndKeywords() to parse the arguments to such a
function.
メソッドテーブルはモジュール定義の構造体から参照されていなければなりません:
static struct PyModuleDef spam_module = {
...
.m_methods = spam_methods,
...
};
This structure, in turn, must be passed to the interpreter in the module's
initialization function. The initialization function must be named
PyInit_name(), where name is the name of the module, and should be the
only non-static item defined in the module file:
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
Note that PyMODINIT_FUNC declares the function as PyObject * return type,
declares any special linkage declarations required by the platform, and for C++
declares the function as extern "C".
PyInit_spam() is called when each interpreter imports its module
spam for the first time. (See below for comments about embedding Python.)
A pointer to the module definition must be returned via PyModuleDef_Init(),
so that the import machinery can create the module and store it in sys.modules.
When embedding Python, the PyInit_spam() function is not called
automatically unless there's an entry in the PyImport_Inittab table.
To add the module to the initialization table, use PyImport_AppendInittab(),
optionally followed by an import of the module:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int
main(int argc, char *argv[])
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* Add a built-in module, before Py_Initialize */
if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
/* Pass argv[0] to the Python interpreter */
status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
if (PyStatus_Exception(status)) {
goto exception;
}
/* Initialize the Python interpreter. Required.
If this step fails, it will be a fatal error. */
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
PyObject *pmodule = PyImport_ImportModule("spam");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'spam'\n");
}
// ... use Python C API here ...
return 0;
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
注釈
If you declare a global variable or a local static one, the module may
experience unintended side-effects on re-initialisation, for example when
removing entries from sys.modules or importing compiled modules into
multiple interpreters within a process
(or following a fork() without an intervening exec()).
If module state is not yet fully isolated,
authors should consider marking the module as having no support for subinterpreters
(via Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED).
A more substantial example module is included in the Python source distribution
as Modules/xxlimited.c. This file may be used as a template or simply
read as an example.
1.5. コンパイルとリンク¶
新しい拡張モジュールを使えるようになるまで、まだ二つの作業: コンパイルと、Python システムへのリンク、が残っています。動的読み込み (dynamic loading) を使っているのなら、作業の詳細は自分のシステムが使っている動的読み込みの形式によって変わるかもしれません; 詳しくは、拡張モジュールのビルドに関する章 (C および C++ 拡張のビルド 章) や、Windows におけるビルドに関係する追加情報の章 (Windows 上での C および C++ 拡張モジュールのビルド 章) を参照してください。
動的読み込みを使えなかったり、モジュールを常時 Python インタプリタの一部にしておきたい場合には、インタプリタのビルド設定を変更して再ビルドしなければならなくなるでしょう。Unixでは、幸運なことにこの作業はとても単純です: 単に自作のモジュールファイル (例えば spammodule.c) を展開したソース配布物の Modules/ ディレクトリに置き、 Modules/Setup.local に自分のファイルを説明する以下の一行:
spam spammodule.o
を追加して、トップレベルのディレクトリで make を実行して、インタプリタを再ビルドするだけです。 Modules/ サブディレクトリでも make を実行できますが、前もって 'make Makefile' を実行して Makefile を再ビルドしておかなければならりません。(この作業は Setup ファイルを変更するたびに必要です。)
モジュールが別のライブラリとリンクされている必要がある場合、ライブラリも設定ファイルに列挙できます。例えば以下のようにします。
spam spammodule.o -lX11
1.6. C から Python 関数を呼び出す¶
これまでは、Python からの C 関数の呼び出しに重点を置いて述べてきました。ところでこの逆: C からの Python 関数の呼び出しもまた有用です。とりわけ、いわゆる "コールバック" 関数をサポートするようなライブラリを作成する際にはこの機能が便利です。ある C インターフェースがコールバックを利用している場合、同等の機能を提供する Python コードでは、しばしば Python プログラマにコールバック機構を提供する必要があります; このとき実装では、 C で書かれたコールバック関数から Python で書かれたコールパック関数を呼び出すようにする必要があるでしょう。もちろん、他の用途も考えられます。
Fortunately, the Python interpreter is easily called recursively, and there is a standard interface to call a Python function. (If you're interested in how to call the Python parser with a particular string as input, see 超高水準レイヤ.)
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;
}
This function must be registered with the interpreter using the
METH_VARARGS flag; this is described in section モジュールのメソッドテーブルと初期化関数. The
PyArg_ParseTuple() function and its arguments are documented in section
拡張モジュール関数でのパラメタ展開.
Py_XINCREF() および Py_XDECREF() は、オブジェクトに対する参照カウントをインクリメント/デクリメントするためのマクロで、 NULL ポインタが渡されても安全に操作できる形式です (とはいえ、上の流れでは temp が NULL になることはありません)。これらのマクロと参照カウントについては、 参照カウント法 で説明しています。
その後、コールバック関数を呼び出す時が来たら、C 関数 PyObject_CallObject() を呼び出します。この関数には二つの引数: Python 関数と Python 関数の引数リストがあり、いずれも任意の Python オブジェクトを表すポインタ型です。引数リストは常にタプルオブジェクトでなければならず、その長さは引数の数になります。Python 関数を引数なしで呼び出すのなら、 NULL か空のタプルを渡します; 単一の引数で関数を呼び出すのなら、単要素 (singleton) のタプルを渡します。 Py_BuildValue() の書式文字列中に、ゼロ個または一個以上の書式化コードが入った丸括弧がある場合、この関数はタプルを返します。以下に例を示します:
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* コールバックを呼び出す */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
PyObject_CallObject() は Python オブジェクトへのポインタを返します: これは Python 関数からの戻り値になります。 PyObject_CallObject() は、引数に対して "参照カウント中立 (reference-count- neutral)" です。上の例ではタプルを生成して引数リストとして提供しており、このタプルは PyObject_CallObject() の呼び出し直後に Py_DECREF() されています。
PyObject_CallObject() の戻り値は「新しい参照」です。
新しく作成したオブジェクトか、参照カウントを増やした既存のオブジェクトを指します。
そのため、グローバル変数に保存するのでなければ、結果に対してどこかで Py_DECREF() を呼び出す必要があります。
戻り値を使わない場合も (特にそのような場合こそ) 必要です。
ただし、その前に戻り値が NULL でないことを確認する必要があります。
NULL なら、Python 関数は例外を送出して終了しています。
PyObject_CallObject() を呼び出した C コード自体が Python から呼び出されている場合は、Python の呼び出し元にエラーを示す値を返す必要があります。
これにより、インタープリタがスタックトレースを表示したり、呼び出し元の Python コードが例外を処理したりできます。
そうすることが不可能、または望ましくない場合は、PyErr_Clear() を呼び出して例外を消去する必要があります。
例えば、次のようにします。
if (result == NULL)
return NULL; /* 呼び出し元にエラーを返す */
...use result...
Py_DECREF(result);
Python のコールバック関数にどのようなインターフェースを用意するかによっては、PyObject_CallObject() に引数リストを渡す必要もあります。
コールバック関数を指定するインターフェースを通じて、Python プログラムが引数リストも渡す場合があります。
その場合は、関数オブジェクトと同じ方法で保存して使えます。
一方、引数リストとして渡す新しいタプルを構築しなければならない場合もあります。
最も簡単な方法は Py_BuildValue() を呼び出すことです。
例えば、整数のイベントコードを渡すには、次のように書けます。
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
return NULL; /* 呼び出し元にエラーを返す */
/* 必要ならここで結果を使う */
Py_DECREF(result);
Py_DECREF(arglist) が呼び出しの直後、エラーチェックよりも前に置かれていることに注意してください! また、厳密に言えば、このコードは完全ではありません: Py_BuildValue() はメモリ不足におちいるかもしれず、チェックしておくべきです。
位置引数とキーワード引数をサポートする PyObject_Call() を使うと、キーワード引数を指定して関数を呼び出すこともできます。
上の例と同じように、Py_BuildValue() を使って辞書を構築します。
PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
1.7. 拡張モジュール関数でのパラメタ展開¶
PyArg_ParseTuple() 関数は以下のように宣言されています:
int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
引数 arg は、Python から C 関数に渡された引数リストを含むタプルオブジェクトでなければなりません。 引数 format は書式文字列で、その構文は Python/C API リファレンスマニュアルの 引数の解釈と値の構築 で説明しています。 残りの引数には、書式文字列によって型が決まる変数のアドレスを渡す必要があります。
PyArg_ParseTuple() は Python 側から与えられた引数が必要な型になっているか調べるのに対し、 PyArg_ParseTuple() は呼び出しの際に渡された C 変数のアドレスが有効な値を持つか調べられないことに注意してください: ここで間違いを犯すと、コードがクラッシュするかもしれませんし、少なくともでたらめなビットをメモリに上書きしてしまいます。慎重に!
呼び出し側に提供されるオブジェクトへの参照はすべて 借用 参照 (borrowed reference) になります; これらのオブジェクトの参照カウントをデクリメントしてはなりません!
以下にいくつかの呼び出し例を示します:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;
ok = PyArg_ParseTuple(args, ""); /* 引数なし */
/* Python での呼び出し: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* 文字列 */
/* Python での呼び出し例: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* long 型の値 2 個と文字列 */
/* Python での呼び出し例: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
/* int 型の値のペアと文字列。
文字列のサイズも取得する */
/* Python での呼び出し例: f((1, 2), 'three') */
{
const char *file;
const char *mode = "r";
int bufsize = 0;
ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
/* 文字列と、省略可能な別の文字列および整数 */
/* Python での呼び出し例:
f('spam')
f('spam', 'w')
f('spam', 'wb', 100000) */
}
{
int left, top, right, bottom, h, v;
ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
&left, &top, &right, &bottom, &h, &v);
/* 長方形と点 */
/* Python での呼び出し例:
f(((0, 0), (400, 300)), (10, 10)) */
}
{
Py_complex c;
ok = PyArg_ParseTuple(args, "D:myfunction", &c);
/* 複素数。
エラー用の関数名も指定する */
/* Python での呼び出し例: myfunction(1+2j) */
}
1.8. 拡張モジュール関数のキーワードパラメタ¶
PyArg_ParseTupleAndKeywords() は、以下のように宣言されています:
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
const char *format, char * const *kwlist, ...);
arg と format パラメタは PyArg_ParseTuple() のものと同じです。 kwdict パラメタはキーワード引数の入った辞書で、 Python ランタイムシステムから第三パラメタとして受け取ります。 kwlist パラメタは各パラメタを識別するための文字列からなる、 NULL 終端されたリストです; 各パラメタ名は format 中の型情報に対して左から右の順に照合されます。成功すると PyArg_ParseTupleAndKeywords() は真を返し、それ以外の場合には適切な例外を送出して偽を返します。
注釈
キーワード引数を使っている場合、タプルは入れ子にして使えません! kwlist 内に存在しないキーワードパラメタが渡された場合、 TypeError の送出を引き起こします。
以下にキーワードを使ったモジュール例を示します。これは Geoff Philbrick (philbrick@hks.com) によるプログラム例をもとにしています:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
int voltage;
const char *state = "a stiff";
const char *action = "voom";
const char *type = "Norwegian Blue";
static char *kwlist[] = {"voltage", "state", "action", "type", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
&voltage, &state, &action, &type))
return NULL;
printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
action, voltage);
printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);
Py_RETURN_NONE;
}
static PyMethodDef keywdarg_methods[] = {
/* The cast of the function is necessary since PyCFunction values
* only take two PyObject* parameters, and keywdarg_parrot() takes
* three.
*/
{"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
"Print a lovely skit to standard output."},
{NULL, NULL, 0, NULL} /* sentinel */
};
static struct PyModuleDef keywdarg_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "keywdarg",
.m_size = 0,
.m_methods = keywdarg_methods,
};
PyMODINIT_FUNC
PyInit_keywdarg(void)
{
return PyModuleDef_Init(&keywdarg_module);
}
1.9. 任意の値を構築する¶
Py_BuildValue() は PyArg_ParseTuple() の対極に位置するものです。この関数は以下のように定義されています:
PyObject *Py_BuildValue(const char *format, ...);
Py_BuildValue() は、 PyArg_ParseTuple() の認識する一連の書式単位に似た書式単位を認識します。ただし (関数への出力ではなく、入力に使われる) 引数はポインタではなく、ただの値でなければなりません。 Python から呼び出された C 関数が返す値として適切な、新たな Python オブジェクトを返します。
PyArg_ParseTuple() とは一つ違う点があります: PyArg_ParseTuple() は第一引数をタプルにする必要があります (Python の引数リストは内部的には常にタプルとして表現されるからです) が、 Py_BuildValue() はタプルを生成するとは限りません。 Py_BuildValue() は書式文字列中に書式単位が二つかそれ以上入っている場合にのみタプルを構築します。書式文字列が空なら、 None を返します。きっかり一つの書式単位なら、その書式単位が記述している何らかのオブジェクトになります。サイズが 0 や 1 のタプル返させたいのなら、書式文字列を丸括弧で囲います。
以下に例を示します (左に呼び出し例を、右に構築される Python 値を示します):
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
1.10. 参照カウント法¶
C や C++のような言語では、プログラマはヒープ上のメモリを動的に確保したり解放したりする責任があります。こうした作業は C では関数 malloc() や free() で行います。C++では本質的に同じ意味で演算子 new や delete が使われます。そこで、以下の議論は C の場合に限定して行います。
malloc() で確保したメモリブロックは、最終的に free() をちょうど 1 回呼び出して、利用可能なメモリのプールに戻す必要があります。
free() を適切なタイミングで呼び出すことが重要です。
free() を呼び出さないままブロックのアドレスを失うと、そのメモリはプログラムが終了するまで再利用できません。
これをメモリリーク (memory leak) と呼びます。
一方、free() を呼び出した後もそのブロックを使い続けると、別の malloc() 呼び出しによるブロックの再利用と競合します。
これを解放済みメモリの使用 (using freed memory) と呼びます。
初期化していないデータへの参照と同様に、コアダンプ、誤った結果、不可解なクラッシュといった悪影響があります。
よくあるメモリリークの原因はコード中の普通でない処理経路です。例えば、ある関数があるメモリブロックを確保し、何らかの計算を行って、再度ブロックを解放するとします。さて、関数の要求仕様を変更して、計算に対するテストを追加すると、エラー条件を検出し、関数の途中で処理を戻すようになるかもしれません。この途中での終了が起きるとき、確保されたメモリブロックは解放し忘れやすいのです。コードが後で追加された場合には特にそうです。このようなメモリリークが一旦紛れ込んでしまうと、長い間検出されないままになることがよくあります: エラーによる関数の終了は、全ての関数呼び出しのに対してほんのわずかな割合しか起きず、その一方でほとんどの近代的な計算機は相当量の仮想記憶を持っているため、メモリリークが明らかになるのは、長い間動作していたプロセスがリークを起こす関数を何度も使った場合に限られるからです。従って、この種のエラーを最小限にとどめるようなコーディング規約や戦略を設けて、不慮のメモリリークを避けることが重要なのです。
Python は malloc() や free() を非常によく利用するため、メモリリークの防止に加え、解放されたメモリの使用を防止する戦略が必要です。このために選ばれたのが参照カウント法 (reference counting) と呼ばれる手法です。参照カウント法の原理は簡単です: 全てのオブジェクトにはカウンタがあり、オブジェクトに対する参照がどこかに保存されたらカウンタをインクリメントし、オブジェクトに対する参照が削除されたらデクリメントします。カウンタがゼロになったら、オブジェクトへの最後の参照が削除されたことになり、オブジェクトは解放されます。
An alternative strategy is called automatic garbage collection.
(Sometimes, reference counting is also referred to as a garbage collection
strategy, hence the use of "automatic" to distinguish the two.) The big
advantage of automatic garbage collection is that the user doesn't need to call
free() explicitly. (Another claimed advantage is an improvement in speed
or memory usage --- this is no hard fact however.) The disadvantage is that for
C, there is no truly portable automatic garbage collector, while reference
counting can be implemented portably (as long as the functions malloc()
and free() are available --- which the C Standard guarantees). Maybe some
day a sufficiently portable automatic garbage collector will be available for C.
Until then, we'll have to live with reference counts.
Python は従来の参照カウント法に加えて、循環参照を検出する仕組みも備えています。 これにより、アプリケーションは直接または間接的な循環参照ができることを心配せずに済みます。 循環参照は、参照カウント法だけで実装したガベージコレクションの弱点です。 循環参照は、直接または間接的に自分自身への参照を持つオブジェクトからなり、循環内の各オブジェクトの参照カウントはゼロではありません。 通常の参照カウント法の実装では、循環そのものへの参照がほかになくなっても、循環内のオブジェクトや、そこから参照されるオブジェクトのメモリを回収できません。
循環参照検出機構はそのようなガベージサイクル (前述したような循環参照オブジェクト) を検出して回収することができます。
gc モジュールそのような検出機構の実行 (collect() 関数) を提供するとともに、設定のためのインタフェースおよび検出機構を実行時に無効にする方法も提供しています。
1.10.1. Python における参照カウント法¶
参照カウントを増減するマクロとして、Py_INCREF(x) と Py_DECREF(x) があります。
Py_DECREF() は、カウントがゼロになるとオブジェクトの解放も行います。
柔軟性を持たせるため、free() を直接呼び出すのではなく、オブジェクトの型オブジェクト (type object) にある関数ポインターを介して呼び出します。
この目的などのために、すべてのオブジェクトは自身の型オブジェクトへのポインターも持っています。
さて、まだ重大な疑問が残っています: いつ Py_INCREF(x) や Py_DECREF(x) を使えばよいのでしょうか? まず、いくつかの用語説明から始めさせてください。まず、オブジェクトは "占有 (own)" されることはありません; しかし、あるオブジェクトに対する参照の所有 own a reference はできます。オブジェクトの参照カウントは、そのオブジェクトが参照の所有を受けている回数と定義されています。参照の所有者は、参照が必要なくなった際に Py_DECREF() を呼び出す役割を担います。参照の所有権は委譲 (transfer) できます。所有参照 (owned reference) の放棄には、渡す、保存する、 Py_DECREF() を呼び出す、という三つの方法があります。所有参照を処理し忘れると、メモリリークを引き起こします。
オブジェクトに対する参照は、借用 (borrow) も可能です。 [2] 参照の借用者は、 Py_DECREF() を呼んではなりません。借用者は、参照の所有者から借用した期間を超えて参照を保持し続けてはなりません。所有者が参照を放棄した後で借用参照を使うと、解放済みメモリを使用してしまう危険があるので、絶対に避けねばなりません。 [3]
参照の借用が参照の所有よりも優れている点は、コードがとりうるあらゆる処理経路で参照を廃棄しておくよう注意しなくて済むことです --- 別の言い方をすれば、借用参照の場合には、処理の途中で関数を終了してもメモリリークの危険を冒すことがない、ということです。逆に、所有よりも不利な点は、ごくまともに見えるコードが、実際には参照の借用元で放棄されてしまった後にその参照を使うかもしれないような微妙な状況があるということです。
Py_INCREF() を呼び出すと、借用参照を所有参照に変更できます。この操作は参照の借用元の状態には影響しません --- Py_INCREF() は新たな所有参照を生成し、参照の所有者が担うべき全ての責任を課します (つまり、新たな参照の所有者は、以前の所有者と同様、参照の放棄を適切に行わねばなりません)。
1.10.2. 所有権にまつわる規則¶
オブジェクトへの参照を関数に渡す場合も、関数から返す場合も、その参照の所有権を一緒に移すかどうかは、関数のインターフェース仕様の一部です。
オブジェクトへの参照を返すほとんどの関数は、参照とともに所有権も渡します。特に、 PyLong_FromLong() や Py_BuildValue() のように、新しいオブジェクトを生成する関数は全て所有権を相手に渡します。オブジェクトが実際には新たなオブジェクトでなくても、そのオブジェクトに対する新たな参照の所有権を得ます。例えば、 PyLong_FromLong() はよく使う値をキャッシュしており、キャッシュされた値への参照を返すことがあります。
ほかのオブジェクトからオブジェクトを取り出す多くの関数も、参照とともに所有権を移します。
例えば PyObject_GetAttrString() がそうです。
ただし、こちらはよく使う関数にも例外があり、少し複雑です。
PyTuple_GetItem()、PyList_GetItem()、PyDict_GetItem()、PyDict_GetItemString() はいずれも、タプル、リスト、辞書から借用した参照を返します。
PyImport_AddModule() は、実際にはオブジェクトを生成して返すことがあるにもかかわらず、借用参照を返します: これが可能なのは、生成されたオブジェクトに対する所有参照は sys.modules に保持されるからです。
オブジェクトへの参照を別の関数に渡す場合、一般的には、関数側は呼び出し手から参照を借用します --- 参照を保存する必要があるなら、関数側は Py_INCREF() を呼び出して独立した所有者になります。とはいえ、この規則には二つの重要な例外: PyTuple_SetItem() と PyList_SetItem() があります。これらの関数は、渡された引数要素に対して所有権を乗っ取り (take over) ます --- たとえ失敗してもです! (PyDict_SetItem() とその仲間は所有権を乗っ取りません --- これらはいわば "普通の" 関数です。)
Python から C 関数が呼び出される際には、C 関数は呼び出し側から引数への参照を借用します。C 関数の呼び出し側はオブジェクトへの参照を所有しているので、借用参照の生存期間が保証されるのは関数が処理を返すまでです。このようにして借用参照を保存したり他に渡したりしたい場合にのみ、 Py_INCREF() を使って所有参照にする必要があります。
Python から呼び出された C 関数が返す参照は所有参照でなければなりません --- 所有権は関数から呼び出し側へと委譲されます。
1.10.3. 薄氷¶
数少ない状況において、一見無害に見える借用参照の利用が問題をひきおこすことがあります。この問題はすべて、インタプリタが非明示的に呼び出され、インタプリタが参照の所有者に参照を放棄させてしまう状況と関係しています。
知っておくべきケースのうち最初の、そして最も重要なものは、リスト要素に対する参照を借りている際に起きる、関係ないオブジェクトに対する Py_DECREF() の使用です。例えば:
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0); /* 不具合あり! */
}
上の関数はまず、 list[0] への参照を借用し、次に list[1] を値 0 で置き換え、最後にさきほど借用した参照を出力しています。何も問題ないように見えますね? でもそうではないのです!
Let's follow the control flow into PyList_SetItem(). The list owns
references to all its items, so when item 1 is replaced, it has to dispose of
the original item 1. Now let's suppose the original item 1 was an instance of a
user-defined class, and let's further suppose that the class defined a
__del__() method. If this class instance has a reference count of 1,
disposing of it will call its __del__() method. Internally,
PyList_SetItem() calls Py_DECREF() on the replaced item,
which invokes replaced item's corresponding
tp_dealloc function. During
deallocation, tp_dealloc calls
tp_finalize, which is mapped to the
__del__() method for class instances (see PEP 442). This entire
sequence happens synchronously within the PyList_SetItem() call.
__del__() メソッドは Python で書かれているため、任意の Python コードを実行できます。
では、bug() 内の item への参照を無効にすることもできるでしょうか。
もちろんできます。
bug() に渡されたリストに __del__() メソッドからアクセスできれば、del list[0] に相当する文を実行できます。
これがそのオブジェクトへの最後の参照であれば、オブジェクトのメモリが解放され、item は無効になります。
問題の原因が分かれば、解決は簡単です。一時的に参照回数を増やせばよいのです。正しく動作するバージョンは以下のようになります:
void
no_bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_INCREF(item);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0);
Py_DECREF(item);
}
これは実際にあった話です。
古いバージョンの Python にはこの種の不具合があり、__del__() メソッドが失敗する理由を突き止めるために、C のデバッガーでかなりの時間を費やした人がいました。
The second case of problems with a borrowed reference is a variant involving
threads. Normally, multiple threads in the Python interpreter can't get in each
other's way, because there is a global lock
protecting Python's entire object space.
However, it is possible to temporarily release this lock using the macro
Py_BEGIN_ALLOW_THREADS, and to re-acquire it using
Py_END_ALLOW_THREADS. This is common around blocking I/O calls, to
let other threads use the processor while waiting for the I/O to complete.
Obviously, the following function has the same problem as the previous one:
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_BEGIN_ALLOW_THREADS
...some blocking I/O call...
Py_END_ALLOW_THREADS
PyObject_Print(item, stdout, 0); /* 不具合あり! */
}
1.10.4. NULL ポインタ¶
一般論として、オブジェクトへの参照を引数にとる関数はユーザが NULL ポインタを渡すとは予想しておらず、渡そうとするとコアダンプになる (か、あとでコアダンプを引き起こす) ことでしょう。一方、オブジェクトへの参照を返すような関数は一般に、例外の発生を示す場合にのみ NULL を返します。引数に対して NULL テストを行わない理由は、関数はしばしば受け取ったオブジェクトを他の関数へと引き渡すからです --- 各々の関数が NULL テストを行えば、冗長なテストが大量に行われ、コードはより低速に動くことになります。
従って、 NULL のテストはオブジェクトの "発生源"、すなわち値が NULL になるかもしれないポインタを受け取ったときだけにしましょう。 malloc() や、例外を送出する可能性のある関数がその例です。
マクロ Py_INCREF() および Py_DECREF() は NULL ポインタのチェックを行いません --- しかし、これらのマクロの変化形である Py_XINCREF() および Py_XDECREF() はチェックを行います。
特定のオブジェクト型について調べるマクロ (Pytype_Check()) は NULL ポインタのチェックを行いません --- 繰り返しますが、様々な異なる型を想定してオブジェクトの型を調べる際には、こうしたマクロを続けて呼び出す必要があるので、個別に NULL ポインタのチェックをすると冗長なテストになってしまうのです。型を調べるマクロには、 NULL チェックを行う変化形はありません。
Python から C 関数を呼び出す機構は、C 関数に渡される引数リスト (例でいうところの args) が決して NULL にならないよう保証しています --- 実際には、常にタプル型になるよう保証しています。[4]
NULL ポインタを Python ユーザレベルに "逃がし" てしまうと、深刻なエラーを引き起こします。
1.11. C++での拡張モジュール作成¶
C++ でも拡張モジュールを作成できますが、いくつか制限があります。
メインプログラム (Python インタープリタ) を C コンパイラでコンパイルしてリンクする場合、コンストラクターを持つグローバルオブジェクトや静的オブジェクトは使えません。
メインプログラムを C++ コンパイラでリンクする場合は問題ありません。
Python インタープリタから呼び出される関数 (特にモジュール初期化関数) は、extern "C" を使って宣言する必要があります。
Python のヘッダーファイルを extern "C" {...} で囲む必要はありません。
シンボル __cplusplus が定義されていれば、ヘッダーファイル自体がこの形式を使います (最近の C++ コンパイラはすべてこのシンボルを定義します)。
1.12. 拡張モジュールに C API を提供する¶
多くの拡張モジュールは単に Python から使える新たな関数や型を提供するだけですが、時に拡張モジュール内のコードが他の拡張モジュールでも便利なことがあります。例えば、あるモジュールでは順序概念のないリストのように動作する "コレクション (collection)" クラスを実装しているかもしれません。ちょうどリストを生成したり操作したりできる C API を備えた標準の Python リスト型のように、この新たなコレクション型も他の拡張モジュールから直接操作できるようにするには一連の C 関数を持っていなければなりません。
一見すると簡単そうです。
関数を書き (もちろん static は付けずに)、適切なヘッダーファイルを用意し、C API の使い方を文書化すればよさそうです。
実際、すべての拡張モジュールを常に Python インタープリタに静的にリンクするのであれば、この方法で動作します。
しかし、モジュールを共有ライブラリとして使う場合、あるモジュールで定義したシンボルが別のモジュールから見えるとは限りません。
可視性の詳細はオペレーティングシステムによって異なります。
Python インタープリタとすべての拡張モジュールで単一のグローバルな名前空間を使うシステムもあれば (例えば Windows)、
モジュールのリンク時にインポートするシンボルを明示的に列挙する必要があるシステム (例えば AIX) や、
複数の方式から選べるシステム (ほとんどの Unix) もあります。
そして、シンボルがどこからでも見える場合でも、呼び出したい関数のあるモジュールがまだロードされていないかもしれません。
従って、可搬性の点からシンボルの可視性には何ら仮定をしてはならないことになります。つまり拡張モジュール中の全てのシンボルは static と宣言せねばなりません。例外はモジュールの初期化関数で、これは (モジュールのメソッドテーブルと初期化関数 で述べたように) 他の拡張モジュールとの間で名前が衝突するのを避けるためです。また、他の拡張モジュールからアクセスを 受けるべきではない シンボルは別のやり方で公開せねばなりません。
Python は、拡張モジュール間で C レベルの情報 (ポインター) を渡すための特別な仕組みとして Capsule を提供しています。 Capsule はポインター (void*) を格納する Python のデータ型です。 作成や内容へのアクセスは C API 経由でしかできませんが、ほかの Python オブジェクトと同様に受け渡せます。 特に、拡張モジュールの名前空間にある名前に代入できます。 ほかの拡張モジュールはそのモジュールをインポートし、その名前に対応する値を取得して、Capsule の中からポインターを取り出せます。
Capsule を使って拡張モジュールの C API を公開する方法はいくつもあります。 関数ごとに専用の Capsule を用意してもよいですし、すべての C API ポインターを配列に格納し、その配列のアドレスを Capsule で公開しても構いません。 ポインターの保存や取得に関わる処理を、コードを提供するモジュールとクライアントモジュールにどう分担させるかにも、様々な方法があります。
どの方法を選ぶにしても、 Capsule の name を正しく設定することは重要です。 PyCapsule_New() は name 引数 (const char*) を取ります。 NULL を name に渡すことも許可されていますが、 name を設定することを強く推奨します。正しく名前を付けられた Capsule はある程度の実行時型安全性を持ちます。名前を付けられていない Capsule を他の Capsule と区別する現実的な方法はありません。
特に、 C API を公開するための Capsule には次のルールに従った名前を付けるべきです:
modulename.attributename
PyCapsule_Import() という便利関数は、 Capsule の名前がこのルールに一致しているときにのみ、簡単に Capsule 経由で公開されている C API をロードすることができます。この挙動により、 C API のユーザーが、確実に正しい C API を格納している Capsule をロードできたことを確かめることができます。
以下の例では、名前を公開するモジュールの作者にほとんどの負荷が掛かりますが、よく使われるライブラリを作る際に適切なアプローチを実演します。このアプローチでは、全ての C API ポインタ (例中では一つだけですが!) を、 Capsule の値となる void ポインタの配列に保存します。拡張モジュールに対応するヘッダファイルは、モジュールの import と C API ポインタを取得するよう手配するマクロを提供します; クライアントモジュールは、C API にアクセスする前にこのマクロを呼ぶだけです。
The exporting module is a modification of the spam module from section
簡単な例. The function spam.system() does not call
the C library function system() directly, but a function
PySpam_System(), which would of course do something more complicated in
reality (such as adding "spam" to every command). This function
PySpam_System() is also exported to other extension modules.
PySpam_System() は普通の C 関数で、ほかと同じく static として宣言します。
static int
PySpam_System(const char *command)
{
return system(command);
}
spam_system() には、ごく簡単な変更を加えます。
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = PySpam_System(command);
return PyLong_FromLong(sts);
}
モジュールの先頭にある以下の行
#include <Python.h>
の直後に、以下の二行を必ず追加してください:
#define SPAM_MODULE
#include "spammodule.h"
The #define is used to tell the header file that it is being included in the
exporting module, not a client module. Finally, the module's mod_exec function must take care of initializing the C API pointer array:
static int
spam_module_exec(PyObject *m)
{
static void *PySpam_API[PySpam_API_pointers];
PyObject *c_api_object;
/* Initialize the C API pointer array */
PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;
/* Create a Capsule containing the API pointer array's address */
c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);
if (PyModule_Add(m, "_C_API", c_api_object) < 0) {
return -1;
}
return 0;
}
Note that PySpam_API is declared static; otherwise the pointer
array would disappear when PyInit_spam() terminates!
からくりの大部分はヘッダファイル spammodule.h 内にあり、以下のようになっています:
#ifndef Py_SPAMMODULE_H
#define Py_SPAMMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
/* spammodule のヘッダーファイル */
/* C API 関数 */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)
/* C API ポインターの総数 */
#define PySpam_API_pointers 1
#ifdef SPAM_MODULE
/* この部分は spammodule.c のコンパイル時に使う */
static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;
#else
/* この部分は spammodule の API を使うモジュールで使う */
static void **PySpam_API;
#define PySpam_System \
(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])
/* エラー時は -1、成功時は 0 を返す。
* エラーがあれば PyCapsule_Import が例外を設定する。
*/
static int
import_spam(void)
{
PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);
return (PySpam_API != NULL) ? 0 : -1;
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* !defined(Py_SPAMMODULE_H) */
All that a client module must do in order to have access to the function
PySpam_System() is to call the function (or rather macro)
import_spam() in its mod_exec function:
static int
client_module_exec(PyObject *m)
{
if (import_spam() < 0) {
return -1;
}
/* additional initialization can happen here */
return 0;
}
この方法の主な欠点は、spammodule.h がやや複雑になることです。
ただし、公開するどの関数でも基本構造は同じなので、一度覚えれば済みます。
Finally it should be mentioned that Capsules offer additional functionality,
which is especially useful for memory allocation and deallocation of the pointer
stored in a Capsule. The details are described in the Python/C API Reference
Manual in the section カプセル and in the implementation of Capsules (files
Include/pycapsule.h and Objects/capsule.c in the Python source
code distribution).
脚注