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!!
>>>

やっとできた。それにしても、初めてのことをやるには、何事もパワーが必要です。えらい疲れてしまいました。