PythonからCプログラムを呼び出す
[更新日:
2022年03月19日
]
Python でプログラミングしていると、
新しいデータ構造を作り上げる場合などに、
C/C++ の力を借りたくなることがあります。
Python から C/C++ コードを呼び出す方法を書きます。
まずは、Cから...
1.ソースコード
次の関数を題材にします。
// hello.c
int add(int x, int y)
{
return x + y;
}
void out(const char* adrs, const char* name)
{
printf("こんにちは、私は %s の %s です。\n", adrs, name);
}
2.wrapper コード
まずモジュール(ライブラリ)名を決めます。
ここでは "hello" とします。
そしてラッパモジュールを書きます。
// helloWrapper.c
#include "Python.h"
extern int add(int, int);
extern void out(const char*, const char*);
PyObject* hello_add(PyObject* self, PyObject* args)
{
int x, y, g;
if (!PyArg_ParseTuple(args, "ii", &x, &y))
return NULL;
g = add(x, y);
return Py_BuildValue("i", g);
}
PyObject* hello_out(PyObject* self, PyObject* args, PyObject* kw)
{
const char* adrs = NULL;
const char* name = NULL;
static char* argnames[] = {"adrs", "name", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "|ss",
argnames, &adrs, &name))
return NULL;
out(adrs, name);
return Py_BuildValue("");
}
static PyMethodDef hellomethods[] = {
{"add", hello_add, METH_VARARGS},
{"out", hello_out, METH_VARARGS | METH_KEYWORDS},
{NULL},
};
void inithello()
{
Py_InitModule("hello", hellomethods);
}
インクルード文 - 必須
最初に "Python.h" をインクルードします。
#include "Python.h"
関数の定義
関数とラッパは一対一に対応させるのが解りやすいでしょう。
例えば wrapper 関数名は "モジュール名_関数名" などと決めます。
関数定義はつぎの二通りおこなっています。
PyObject* hello_add(PyObject* self, PyObject* args)
PyObject* hello_out(PyObject* self, PyObject* args, PyObject* kwargs)
後の定義は、キーワード引数を利用する場合です。
引数の解読
Python からの呼び出しが"位置による引数"の場合は次の関数を使います。
PyArg_ParseTuple(PyObject* args, const char* fmt, ...)
また呼び出しが"キーワード引数"の場合は次の関数を使います。
PyArg_ParseTupleAndKeywords(PyObject* args, const char* fmt,
const char* argnames, ...)
"const char* fmt" は引数の変換書式です。
printf (C の関数) の書式にちょっと似ています。
書式 | Python型 | C型 | 説明 |
"c" | 文字列 | char | 一文字 |
"s" | 文字列 | char* | 文字列 |
"i" | 整数 | int | 整数 |
"l" | 整数 | long | 整数 |
"f" | 実数 | float | 浮動小数点数 |
"d" | 実数 | double | 浮動小数点数 |
"|" |   |   | オプション開始 |
|
戻り値
関数の戻り値は次の様に設定します。
return Py_BuildValue(const char* fmt, ...);
戻り値がない場合はヌル文字を返します。
return Py_BuildValue("");
NULL を返した場合は実行エラーになります。
初期化関数
最初に関数の対応テーブルを作ります。
static PyMethodDef hellomethods[] = {
{"add", hello_add, METH_VARARGS},
{"out", hello_out, METH_VARARGS | METH_KEYWORDS},
{NULL},
};
ここで Python 呼び出し名とラッパ関数名の対応を付けます。
"add", "out" は python メソッド名です。
"hello_add", "hello_out" はラッパ関数名です。
"hello_out"はキーワード付きなので "METH_KEYWORDS" を論理和します。
コンパイルすると、ここで
helloWrapper.c:37: --- hello_out の行
warning: initialization from incompatible pointer type
と警告が出ます。
プロトタイプ宣言不一致に対する警告のようなので特に問題は無いと思います。
初期化関数はつぎのように書きます。
void inithello()
{
Py_InitModule("hello", hellomethods);
}
モジュール名が "hello" の場合、初期化関数名は "inithello" とします。
Python でモジュールが import された時に呼び出されます。
3.ビルド
コンパイルし、共有ライブラリを作成します。
$ gcc -fpic -o hello.o -c hello.c
$ gcc -fpic -I/usr/include/python -o helloWrapper.o -c helloWrapper.c
$ gcc -shared hello.o helloWrapper.o -o hellomodule.so
ここで作成するライブラリ名はモジュール名と一致しなければなりません。
モジュール名が "hello" なら、
ライブラリ名は "hellomodule.so" となります。
インクルードヘッダのパスは環境によって違います。
4.実行
以下のように実行し確認します。
$ /usr/bin/python
>>> import hello
>>> hello.add(2, 3)
5
>>> hello.out("大原", "麗子")
今日は、私は 大原 の 麗子 です。
>>>^D
$
このようにできたら成功と言えます。
5.参考資料
「Python入門」 Mark Lutz
- ISBN4-900900-55-9 O'REILLY
「Pythonテクニカルリファレンス」 David M.Beazley
- ISBN4-89471-221-0 ピアソン・エデュケーション
|