概要
Pythonで動いているプログラムから、C/C++で書かれたプログラムを呼び出す方法について記載する。
手法は以下の3パターン
- Python C API
- Cython
- boost.python
記事が長くなってしまいそうなので、手法ごとに記事を分けて記載することにする。
今回は「Python C API」について記載する。
ソースコード公開先
Python C API
Python C APIとは、PythonモジュールをC/C++で記述可能な標準ライブラリ。
実際にPythonから呼び出す際は事前にビルドしておく必要がある。
モジュール化したいC/C++のソースコード
まずはソースコード全体。
#include <Python.h>
static PyObject* c_hello_method(PyObject* self, PyObject* args) {
printf("HELLO TEST\n");
return Py_None;
}
int c_multiplication(int a, int b) {
return a * b;
}
static PyObject* wrap_c_multiplication(PyObject* self, PyObject* args) {
int a, b, c;
if (!PyArg_ParseTuple(args, "ii", &a, &b) {
return NULL;
}
c = c_multiplication(a, b);
return Py_BuildValue("i", c);
}
static PyMethodDef Cmethods[] = {
{"c_hello", c_hello_method, METH_NOARGS, "HELLO TEST"},
{"c_multiplication", wrap_c_multiplication, METH_VARARGS, "Multiply the two values"},
{ NULL, NULL, 0, NULL}
};
static struct PyModuleDef Cmodule = {
PyModuleDef_HEAD_INIT,
"test_C_module",
"Python3 C API Module(test_C_module)",
-1,
Cmethods
}
PyMODINIT_FUNC PyInit_test_C_module(void) {
return PyModule_Create(&Cmodule);
}
解説
必要なヘッダファイルをインクルードする。
Python C APIを使用する際は、C/C++側のソースファイルで、Python.h
をインクルードする必要がある。
#include <Python.h>
// パスが通ってない場合は絶対パスでもOK
#include "/usr/include/python3.8/Python.h"
Pythonで実行したいC/C++の処理を実装する
まずはPythonで実行したいC/C++の関数の内容を記述する。
関数はラッパー関数を用意してもよいし、PyObject型の関数に直接処理内容を記述する方法でもよい。
/* Pythonで実行したいC/C++の処理を記述する */
// ラッパー関数無しで直接記述する方法
static PyObject* c_hello_method(PyObject* self, PyObject* args) {
printf("HELLO TEST\n");
return Py_None; //戻り値は無し
}
// ラッパー関数有りで呼び出す方法
int c_multiplication(int a, int b) {
return a * b;
}
static PyObject* wrap_c_multiplication(PyObject* self, PyObject* args) {
int a, b, c;
// 型を決定しつつ引数を受け取る(PyObject型→int型 int型)
if (!PyArg_ParseTuple(args, "ii", &a, &b) {
return NULL;
}
// 目的の関数を実行
c = c_multiplication(a, b);
// 型を決定しつつ戻り値を返却(int型→PyObject型)
return Py_BuildValue("i", c);
}
Pythonから呼び出すための関数定義
実行したい関数が用意出来たら、それらをPython側に伝えるための定義を行う。
static PyMethodDef Cmethods[] = {
// {"Pythonで使う際の関数名", Python側に実装したいC/C++関数, 引数指定, "関数の説明"}
{"c_hello", c_hello_method, METH_NOARGS, "HELLO TEST"},
{"c_multiplication", wrap_c_multiplication, METH_VARARGS, "Multiply the two values"},
{ NULL, NULL, 0, NULL}
};
Pythonモジュールとして使用するための定義を行う
Pythonモジュールとしてimportするために必要なモジュールの情報を定義する。
// モジュール名の定義
static struct PyModuleDef Cmodule = {
PyModuleDef_HEAD_INIT,
"test_C_module", // これがモジュール名となる
"Python3 C API Module(test_C_module)", // モジュールについての説明文
-1,
Cmethods // 関数定義の名前
}
// モジュールの初期化処理
PyMODINIT_FUNC PyInit_test_C_module(void) {
return PyModule_Create(&Cmodule);
コンパイルする
C/C++のソースコードが出来たらpythonで使用するために*.so
ファイルとしてコンパイルする。
from distutils.core import setup, Extension
module= Extension(
"test_C_module", # モジュール名
sources= ["test_c_mod.c"] # 対象のC/C++ソースファイル
)
setup(name= "test_C_module", # pipに登録する際の名前
version= "1.0.0", # ライブラリのバージョン
ext_modules= [module] # 上記で記述したモジュールの情報
)
コンパイルを実行
コンパイルを実行すると、./build
ディレクトリが生成されその中に*.so
ファイルが生成される。
# コンパイル実行
$ python3 setup.py build
# コンパイル実行→pipの管轄に追加
$ python3 setup.py install
$ pip freeze
実際に使用する
生成された*.so
ファイルを参照可能なディレクトリに移動しておく。
(pipの管轄に追加した場合は不要)
import test_C_module
test_C_module.c_hello()
c= test_C_module.c_multiplication(2, 3)
print(c)
以上。
次回は、**「Cython」**を使用した方法について書く予定です。