前章では、 Python を拡張する方法、すなわち C 関数のライブラリを Python に結びつけて機能を拡張する方法について述べました。 同じようなことを別の方法でも実行できます: それは、自分の C/C++ アプリケーションに Python を埋め込んで機能を強化する、というものです。 埋め込みを行うことで、アプリケーションの何らかの機能を C や C++ の代わりに Python で実装できるようになります。 埋め込みは多くの用途で利用できます; ユーザが Python でスクリプトを書き、アプリケーションを自分好みに仕立てられるようにする、というのが その一例です。プログラマが、特定の機能を Python でより楽に書ける場合に自分自身のために埋め込みを行うこともできます。
Python の埋め込みは Python の拡張と似ていますが、全く同じというわけではありません。その違いは、Python を拡張した場合には アプリケーションのメインプログラムは依然として Python インタプリタである一方、 Python を組み込みんだ場合には、メインプログラムには Python が関係しない — その代わりに、アプリケーションのある一部分が時折 Python インタプリタを呼び出して何らかの Python コードを 実行させる — かもしれない、ということです。
従って、 Python の埋め込みを行う場合、自作のメインプログラムを提供しなければなりません。メインプログラムがやらなければならないことの一つに、 Python インタプリタの初期化があります。とにかく少なくとも関数 Py_Initialize() を呼び出さねばなりません。オプションとして、Python 側にコマンドライン引数を渡すために関数呼び出しを行います。その後、アプリケーションのどこでもインタプリタを呼び出せるようになります。
インタプリタを呼び出すには、異なるいくつかの方法があります: Python 文が入った文字列を PyRun_SimpleString() に渡す、 stdio ファイルポインタとファイル名 (これはエラーメッセージ内でコードを識別するためだけのものです) を PyRun_SimpleFile() に渡す、といった具合です。これまでの各章で説明した低水準の操作を呼び出して、Python オブジェクトを 構築したり使用したりもできます。
Python の埋め込みを行っている簡単なデモは、ソース配布物の Demo/embed/ ディレクトリにあります。
参考
Python の埋め込みの最も簡単な形式は、超高水準インタフェースの利用です。このインタフェースは、アプリケーションとやり取りする必要がない Python スクリプトを実行するためのものです。例えばこれは、一つのファイル上で何らかの操作を実現するのに利用できます。
#include <Python.h>
int
main(int argc, char *argv[])
{
Py_Initialize();
PyRun_SimpleString("from time import time,ctime\n"
"print 'Today is',ctime(time())\n");
Py_Finalize();
return 0;
}
上のコードでは、まず Python インタプリタを Py_Initialize() で起動し、続いてハードコードされた Python スクリプトで日付と時間の出力を実行します。その後、 Py_Finalize() の呼び出しでインタプリタを終了し, プログラムの終了に続きます。 実際のプログラムでは、Python スクリプトを他のソース、おそらくテキストエディタルーチンやファイル、データベースから取り出したいと 考えるかもしれません。Python コードをファイルから取り出すには、 PyRun_SimpleFile() 関数を使うのがよいでしょう。 この関数はメモリを確保して、ファイルの内容をロードする手間を省いてくれます。
高水準インタフェースは、断片的な Python コードをアプリケーションから実行できるようにしてくれますが、アプリケーションと Python コードの 間でのデータのやり取りは、控えめに言っても煩わしいものです。データのやり取りをしたいなら、より低水準のインタフェース呼び出しを 利用しなくてはなりません。より多く C コードを書かねばならない代わりに、ほぼ何でもできるようになります。
Python の拡張と埋め込みは、趣旨こそ違え、同じ作業であるということに注意せねばなりません。これまでの章で議論してきたトピックの ほとんどが埋め込みでもあてはまります。これを示すために、 Python から C への拡張を行うコードが実際には何をするか考えてみましょう:
Python を埋め込む場合には、インタフェースコードが行う作業は以下のようになります:
一見して分かるように、データ変換のステップは、言語間でデータを転送する方向が変わったのに合わせて単に入れ替えただけです。 唯一の相違点は、データ変換の間にあるルーチンです。拡張を行う際には C ルーチンを呼び出しますが、埋め込みの際には Python ルーチンを呼び出します。
この章では、Python から C へ、そしてその逆へとデータを変換する方法については議論しません。また、正しい参照の使い方やエラーの 扱い方についてすでに理解しているものと仮定します。これらの側面についてはインタプリタの拡張と何ら変わるところが ないので、必要な情報については以前の章を参照できます。
最初に例示するプログラムは、Python スクリプト内の関数を実行するためのものです。超高水準インタフェースに関する節で挙げた例と同様に、Python インタプリタはアプリケーションと直接やりとりはしません (が、次の節でやりとりするよう変更します)。
Python スクリプト内で定義されている関数を実行するためのコードは以下のようになります:
#include <Python.h>
int
main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict, *pFunc;
PyObject *pArgs, *pValue;
int i;
if (argc < 3) {
fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
return 1;
}
Py_Initialize();
pName = PyString_FromString(argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */
if (pFunc && PyCallable_Check(pFunc)) {
pArgs = PyTuple_New(argc - 3);
for (i = 0; i < argc - 3; ++i) {
pValue = PyInt_FromLong(atoi(argv[i + 3]));
if (!pValue) {
Py_DECREF(pArgs);
Py_DECREF(pModule);
fprintf(stderr, "Cannot convert argument\n");
return 1;
}
/* pValue reference stolen here: */
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != NULL) {
printf("Result of call: %ld\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);
}
else {
Py_DECREF(pFunc);
Py_DECREF(pModule);
PyErr_Print();
fprintf(stderr,"Call failed\n");
return 1;
}
}
else {
if (PyErr_Occurred())
PyErr_Print();
fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
}
else {
PyErr_Print();
fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
return 1;
}
Py_Finalize();
return 0;
}
このコードは argv[1] を使って Python スクリプトをロードし、 argv[2] 内に指定された名前の関数を呼び出します。 関数の整数引数は argv 配列中の他の値になります。このプログラムをコンパイルしてリンクし (できた実行可能形式を call と呼びましょう)、以下のような Python スクリプトを実行することにします:
def multiply(a,b):
print "Will compute", a, "times", b
c = 0
for i in range(0, a):
c = c + b
return c
実行結果は以下のようになるはずです:
$ call multiply multiply 3 2
Will compute 3 times 2
Result of call: 6
この程度の機能を実現するにはプログラムがいささか大きすぎますが、ほとんどは Python から C へのデータ変換やエラー報告のための コードです。Python の埋め込みという観点から最も興味深い部分は以下のコード、
Py_Initialize();
pName = PyString_FromString(argv[1]);
/* pName のエラーチェックは省略している */
pModule = PyImport_Import(pName);
から始まる部分です。
インタプリタの初期化後、スクリプトは PyImport_Import() を使って読み込まれます。このルーチンは Python 文字列を引数に取る必要があり、データ変換ルーチン PyString_FromString() で構築します。
pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc は新たな参照 */
if (pFunc && PyCallable_Check(pFunc)) {
...
}
Py_XDECREF(pFunc);
ひとたびスクリプトが読み込まれると、 PyObject_GetAttrString() を使って必要な名前を取得 できます。名前がスクリプト中に存在し、取得したオブジェクトが呼び出し可能オブジェクトであれば、このオブジェクトが関数であると 考えて差し支えないでしょう。そこでプログラムは定石どおりに引数のタプル構築に進みます。その後、Python 関数を以下のコードで呼び出します:
pValue = PyObject_CallObject(pFunc, pArgs);
関数が処理を戻す際、 pValue は NULL になるか、関数の戻り値への参照が入っています。値を調べた後には忘れずに参照を解放してください。
ここまでは、埋め込み Python インタプリタはアプリケーション本体の機能にアクセスする手段がありませんでした。 Python API を使うと、埋め込みインタプリタを拡張することでアプリケーション本体へのアクセスを可能にします。つまり、アプリケーションで提供されているルーチンを使って、 埋め込みインタプリタを拡張するのです。複雑なことのように思えますが、それほどひどいわけではありません。さしあたって、アプリケーションが Python インタプリタを起動したということをちょっと忘れてみてください。その代わり、アプリケーションがサブルーチンの集まりで、あたかも普通の Python 拡張モジュールを書くかのように、Python から各ルーチンにアクセスできるようにするグルー(glue, 糊) コードを書くと考えてください。例えば以下のようにです:
static int numargs=0;
/* アプリケーションのコマンドライン引数の個数を返す */
static PyObject*
emb_numargs(PyObject *self, PyObject *args)
{
if(!PyArg_ParseTuple(args, ":numargs"))
return NULL;
return Py_BuildValue("i", numargs);
}
static PyMethodDef EmbMethods[] = {
{"numargs", emb_numargs, METH_VARARGS,
"Return the number of arguments received by the process."},
{NULL, NULL, 0, NULL}
};
上のコードを main() 関数のすぐ上に挿入します。また、以下の二つの文を Py_Initialize() の直後 に挿入します:
numargs = argc;
Py_InitModule("emb", EmbMethods);
これら二つの行は numargs 変数を初期化し、埋め込み Python インタプリタから emb.numargs() 関数に アクセスできるようにします。これらの拡張モジュール関数を使うと、 Python スクリプトは
import emb
print "Number of arguments", emb.numargs()
のようなことができます。
実際のアプリケーションでは、こうしたメソッドでアプリケーション内の API を Python に公開することになります。
C++ プログラム中にも Python を埋め込めます; 厳密に言うと、どうやって埋め込むかは使っているC++ 処理系の詳細に依存します; 一般的には、メインプログラムをC++で書き、C++ コンパイラを使ってプログラムをコンパイル・リンクする必要があるでしょう。 Python 自体を C++でコンパイルしなおす必要はありません。
Python ソースと一緒についてくる configure スクリプトは動的にリンクされる拡張モジュールが必要とするシンボルを公開するよう ただしく Python をビルドしますが、この機能は Python ライブラリを静的に埋め込むようなアプリケーションには継承されません。少なくとも Unix ではそうです。これは、アプリケーションが静的な実行時ライブラリ (libpython.a) にリンクされていて、かつ (.so ファイルとして実装されている) 動的ロードされるような拡張モジュールをロードする必要がある場合に起きる問題です。
問題になるのは、拡張モジュールが使うあるエントリポイントが Python ランタイムだけで定義されているという状況です。 埋め込みを行うアプリケーション側がこうしたエントリポイントを全く使わない場合、リンカによってはエントリポイントを最終的に 生成される実行可能形式のシンボルテーブル内に含めません。こうした場合、リンカに追加のオプションを与えて、これらのシンボルを 除去しないよう教える必要があります。
プラットフォームごとに正しいオプションを決めるのはかなり困難です、とはいえ、幸運なことに、オプションは Python のビルド設定内にすでに あります。インストール済みの Python インタプリタからオプションを取り出すには、対話インタプリタを起動して、以下のような短いセッションを実行します:
>>> import distutils.sysconfig
>>> distutils.sysconfig.get_config_var('LINKFORSHARED')
'-Xlinker -export-dynamic'
表示された文字列の内容が、ビルド時に使うべきオプションです。文字列が空であれば、特に追加すべきオプションはありません。 LINKFORSHARED の定義内容は、 Python のトップレベル Makefile 内の同名の変数に対応しています。