PythonからMinGWで作ったCの関数を呼んでみる
まずはやっぱりオリジナルマニュアルからが基本だろう
C プログラムの書き方を知っているなら、Python に新たな組み込みモジュールを追加するのはきわめて簡単です。この新たなモジュール、拡張モジュール (extention module) を使うと、Python が直接行えない二つのこと: 新しい組み込みオブジェクトの実装、そして全ての C ライブラリ関数とシステムコールに対する呼び出し、ができるようになります。
( Python インタプリタの拡張と埋め込み – Python ドキュメント リリース 2.5)
「きわめて簡単」と書かれているが自分にとっては難解だ。途中で挫折して他をあたることにした。なので、いつものようにメモしておく。
以下は参考にさせてもらったサイト
実験環境
- Windows XP SP3
- Python 2.6.5
- gcc 3.4.5
Helloを表示させる
文字列を表示するだけの超簡単な例でやってみることにした。
/* extsamp.c */ #include <Python.h> /* モジュールの関数 */ static PyObject * hello(void) { printf("Hello World!!\n"); Py_RETURN_NONE; } /* モジュールのメソッドテーブル */ static PyMethodDef methods[] = { {"hello", (PyCFunction)hello, METH_VARARGS, "print hello world.\n"}, {NULL, NULL, 0, NULL} }; /* モジュールの初期化関数 */ PyMODINIT_FUNC initextsamp(void) { (void)Py_InitModule("extsamp", methods); }
- ファイルの最初の行は #include <Python.h> にする
“Python.h" はいくつかの標準ヘッダファイル: <stdio.h>、 <string.h>、 <errno.h>、および <stdlib.h> をインクルードしている
- モジュールのメソッドテーブル
順番に
- 関数名
- アドレス 。PyCFunctionでキャストしないとコンパイルでWarningがでた。PyCFunction の値は PyObject* パラメタを二つだけしか引数に取らない。それ以外の場合はキャストが必要。
- C 関数が使う呼び出し規約をインタプリタに教えるためのフラグ。通常この値は"METH_VARARGS" か “METH_VARARGS | METH_KEYWORDS" 。0 は旧式のPyArg_ParseTuple() の変化形が使われることを意味します。???
- モジュールの初期化関数
- initの後にモジュール名を付けたものが関数名となる。モジュール名はextsampなので、initextsampが関数名。
コンパイル
コンパイルするには、ヘッダーファイルファイル “Python.h"を読み込む位置をgccに教えてあげる必要がある。自分の環境ではPythonはC:\Python26にインストールしてある。explorerでフォルダをみると、C:\Python26\includeが見つかる。たくさんのC/C++ヘッダーファイルが置いてある。参考にしたネット情報を参考にコンパイルする。
> gcc -fPIC -c extsamp.c -I/python26/include extsamp.c:1: warning: -fPIC ignored for target (all code is position independent )
warningが1つでた。「すべてのコードは位置独立なので、-fPICは無視された」らしい。Linux で共有ライブラリを作るときは PIC でコンパイルするが、Windows(PEフォーマット)では、常に位置独立コード (position-independent code; PIC) が生成されるので、 -fPIC オプションは必要無いことがわかったので、もういちどコンパイルする。
> gcc -c extsamp.c -I/python26/include > ls extsamp.c extsamp.o
共有DLLを作る
共有DLLを作るには libpython26.a をリンクさせる必要がある。このファイルは C:\Python26\libs にあるので-Lオプションでgccに教えてあげる。-lオプションでリンクファイル名を指定するときlibを省いて -lpython26 とする。
共有DLLのファイル名は extsamp.pydとするため -o extsamp.pyd のオプションを付ける。拡張子をpydとするのは、「システムライブラリ .dll と、Python インタフェースとなる自作のモジュールとの混同を避けるため」とのことだ。
> gcc -shared -o extsamp.pyd extsamp.o -L/python26/libs -lpython26 > ls extsamp.c extsamp.o extsamp.pyd
作ったモジュールをインポートして実行してみる
> python Python 2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import extsamp >>> extsamp.hello() Hello World!! >>>
やっとできた。それにしても、初めてのことをやるには、何事もパワーが必要です。えらい疲れてしまいました。