1. 他のアプリケーションへの Python の埋め込み
*********************************************

前章では、 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/C API リファレンスマニュアル
     Python C インターフェースの詳細はこのマニュアルに書かれています。
     必要な情報の大部分はここにあるはずです。


1.1. 高水準の埋め込み
=====================

Python の埋め込みの最も簡単な形式は、超高水準インターフェースの利用で
す。このインターフェースは、アプリケーションとやり取りする必要がない
Python スクリプトを実行するためのものです。例えばこれは、一つのファイ
ル上で何らかの操作を実現するのに利用できます。

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   int
   main(int argc, char *argv[])
   {
       wchar_t *program = Py_DecodeLocale(argv[0], NULL);
       if (program == NULL) {
           fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
           exit(1);
       }
       Py_SetProgramName(program);  /* optional but recommended */
       Py_Initialize();
       PyRun_SimpleString("from time import time,ctime\n"
                          "print('Today is', ctime(time()))\n");
       if (Py_FinalizeEx() < 0) {
           exit(120);
       }
       PyMem_RawFree(program);
       return 0;
   }

"Py_SetProgramName()" は "Py_Initialize()" の前に呼び出す必要がありま
す。これによりインタプリタにランタイムライブラリへのパスを伝えることが
出来ます。続いて、Python インタプリタを "Py_Initialize()" で初期化し、
続いてハードコードされた Python スクリプトで日付と時間の出力を実行しま
す。その後、 "Py_FinalizeEx()" の呼び出しでインタプリタを終了し, プロ
グラムの終了に続きます。実際のプログラムでは、Python スクリプトを他の
ソース、おそらくテキストエディタルーチンやファイル、データベースから取
り出したいと考えるかもしれません。Python コードをファイルから取り出す
には、 "PyRun_SimpleFile()" 関数を使うのがよいでしょう。この関数はメモ
リを確保して、ファイルの内容をロードする手間を省いてくれます。


1.2. 超高水準の埋め込みから踏み出す: 概要
=========================================

高水準インターフェースは、断片的な Python コードをアプリケーションから
実行できるようにしてくれますが、アプリケーションと Python コードの間で
のデータのやり取りは、控えめに言っても煩わしいものです。データのやり取
りをしたいなら、より低水準のインターフェース呼び出しを利用しなくてはな
りません。より多く C コードを書かねばならない代わりに、ほぼ何でもでき
るようになります。

Python の拡張と埋め込みは、趣旨こそ違え、同じ作業であるということに注
意せねばなりません。これまでの章で議論してきたトピックのほとんどが埋め
込みでもあてはまります。これを示すために、 Python から C への拡張を行
うコードが実際には何をするか考えてみましょう:

1. データ値を Python から C に変換する。

2. 変換された値を使って C ルーチンの関数呼び出しを行い、

3. 呼び出しで得られたデータ値 C から Python に変換する。

Python を埋め込む場合には、インターフェースコードが行う作業は以下のよ
うになります:

1. データ値を C から Python に変換する。

2. 変換された値を使って Python インターフェースルーチンの関数呼び出し
   を行い、

3. 呼び出しで得られたデータ値 Python から C に変換する。

一見して分かるように、データ変換のステップは、言語間でデータを転送する
方向が変わったのに合わせて単に入れ替えただけです。唯一の相違点は、デー
タ変換の間にあるルーチンです。拡張を行う際には C ルーチンを呼び出しま
すが、埋め込みの際には Python ルーチンを呼び出します。

この章では、Python から C へ、そしてその逆へとデータを変換する方法につ
いては議論しません。また、正しい参照の使い方やエラーの扱い方についてす
でに理解しているものと仮定します。これらの側面についてはインタプリタの
拡張と何ら変わるところがないので、必要な情報については以前の章を参照で
きます。


1.3. 純粋な埋め込み
===================

最初に例示するプログラムは、Python スクリプト内の関数を実行するための
ものです。超高水準インターフェースに関する節で挙げた例と同様に、Python
インタプリタはアプリケーションと直接やりとりはしません (が、次の節でや
りとりするよう変更します)。

Python スクリプト内で定義されている関数を実行するためのコードは以下の
ようになります:

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   int
   main(int argc, char *argv[])
   {
       PyObject *pName, *pModule, *pFunc;
       PyObject *pArgs, *pValue;
       int i;

       if (argc < 3) {
           fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
           return 1;
       }

       Py_Initialize();
       pName = PyUnicode_DecodeFSDefault(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 = PyLong_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", PyLong_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;
       }
       if (Py_FinalizeEx() < 0) {
           return 120;
       }
       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 = PyUnicode_DecodeFSDefault(argv[1]);
   /* Error checking of pName left out */
   pModule = PyImport_Import(pName);

インタプリタの初期化後、スクリプトは "PyImport_Import()" を使って読み
込まれます。このルーチンは Python 文字列を引数に取る必要があり、データ
変換ルーチン "PyUnicode_FromString()" で構築します。

   pFunc = PyObject_GetAttrString(pModule, argv[2]);
   /* pFunc is a new reference */

   if (pFunc && PyCallable_Check(pFunc)) {
       ...
   }
   Py_XDECREF(pFunc);

ひとたびスクリプトが読み込まれると、 "PyObject_GetAttrString()" を使っ
て必要な名前を取得できます。名前がスクリプト中に存在し、取得したオブジ
ェクトが呼び出し可能オブジェクトであれば、このオブジェクトが関数である
と考えて差し支えないでしょう。そこでプログラムは定石どおりに引数のタプ
ル構築に進みます。その後、Python 関数を以下のコードで呼び出します:

   pValue = PyObject_CallObject(pFunc, pArgs);

関数が処理を戻す際、 "pValue" は "NULL" になるか、関数の戻り値への参照
が入っています。値を調べた後には忘れずに参照を解放してください。


1.4. 埋め込まれた Python の拡張
===============================

ここまでは、埋め込み Python インタプリタはアプリケーション本体の機能に
アクセスする手段がありませんでした。 Python API を使うと、埋め込みイン
タプリタを拡張することでアプリケーション本体へのアクセスを可能にします
。つまり、アプリケーションで提供されているルーチンを使って、埋め込みイ
ンタプリタを拡張するのです。複雑なことのように思えますが、それほどひど
いわけではありません。さしあたって、アプリケーションが Python インタプ
リタを起動したということをちょっと忘れてみてください。その代わり、アプ
リケーションがサブルーチンの集まりで、あたかも普通の Python 拡張モジュ
ールを書くかのように、Python から各ルーチンにアクセスできるようにする
グルー(glue, 糊) コードを書くと考えてください。例えば以下のようにです:

   static int numargs=0;

   /* Return the number of arguments of the application command line */
   static PyObject*
   emb_numargs(PyObject *self, PyObject *args)
   {
       if(!PyArg_ParseTuple(args, ":numargs"))
           return NULL;
       return PyLong_FromLong(numargs);
   }

   static PyMethodDef EmbMethods[] = {
       {"numargs", emb_numargs, METH_VARARGS,
        "Return the number of arguments received by the process."},
       {NULL, NULL, 0, NULL}
   };

   static PyModuleDef EmbModule = {
       PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
       NULL, NULL, NULL, NULL
   };

   static PyObject*
   PyInit_emb(void)
   {
       return PyModule_Create(&EmbModule);
   }

上のコードを "main()" 関数のすぐ上に挿入します。また、以下の二つの文を
"Py_Initialize()" の呼び出しの前に挿入します:

   numargs = argc;
   PyImport_AppendInittab("emb", &PyInit_emb);

これら二つの行は "numargs" 変数を初期化し、埋め込み Python インタプリ
タから "emb.numargs()" 関数にアクセスできるようにします。これらの拡張
モジュール関数を使うと、 Python スクリプトは以下のようなことができます
。

   import emb
   print("Number of arguments", emb.numargs())

実際のアプリケーションでは、こうしたメソッドでアプリケーション内の API
を Python に公開することになります。


1.5. C++による Python の埋め込み
================================

C++ プログラム中にも Python を埋め込めます; 厳密に言うと、どうやって埋
め込むかは使っているC++ 処理系の詳細に依存します; 一般的には、メインプ
ログラムをC++で書き、C++ コンパイラを使ってプログラムをコンパイル・リ
ンクする必要があるでしょう。 Python 自体を C++でコンパイルしなおす必要
はありません。


1.6. Unix 系システムにおけるコンパイルとリンク
==============================================

Python インタプリタをアプリケーションに埋め込むためにコンパイラ (とリ
ンカ) に渡すべき正しいフラグを見出すのは簡単でないかもしれません。これ
は特に、Python がライブラリモジュールに対してリンクされた C 動的拡張
(".so" ファイル) として実装されたものをロードする必要があるためです。

必要なコンパイル・リンクのオプションを知るために、
"python*X.Y*-config" スクリプトが使えます(これは Python インストール時
に生成されたもので、 "python3-config" スクリプトも利用出来るかもしれま
せん)。このスクリプトにはオプションが多くありますが、直接的に有用なの
はこれでしょう:

* "pythonX.Y-config --cflags" は推奨のコンパイルオプションを出力します
  :

     $ /opt/bin/python3.11-config --cflags
     -I/opt/include/python3.11 -I/opt/include/python3.11 -Wsign-compare  -DNDEBUG -g -fwrapv -O3 -Wall

* "pythonX.Y-config --ldflags --embed" は推奨のリンクオプションを出力
  します:

     $ /opt/bin/python3.11-config --ldflags --embed
     -L/opt/lib/python3.11/config-3.11-x86_64-linux-gnu -L/opt/lib -lpython3.11 -lpthread -ldl  -lutil -lm

注釈:

  複数 Python バージョン共存(とりわけシステムの Python とあなた自身で
  ビルドした Python)での混乱を避けるために、上での例のように
  "python*X.Y*-config" は絶対パスで起動したほうが良いです。

もしこの手順でうまくいかなければ(たしかにこれは全ての Unix 的なプラッ
トフォームで動作することを保障するものではないですが、 bug reports は
歓迎です)、あなたのシステムのダイナミックリンクについてのドキュメント
を読み、Python の "Makefile" のコンパイルオプションを調べる必要がある
でしょう(Makefile の場所を調べるには
"sysconfig.get_makefile_filename()" を使ってください)。この場合、
"sysconfig" モジュールが役に立つ道具になります。これによってあなたが付
け加えたいコンパイル・リンクのオプション構成をプログラム的に抽出できま
す。例えば:

   >>> import sysconfig
   >>> sysconfig.get_config_var('LIBS')
   '-lpthread -ldl  -lutil'
   >>> sysconfig.get_config_var('LINKFORSHARED')
   '-Xlinker -export-dynamic'
