【Python】PythonからC/C++で書かれたソースコードを呼び出す

# 概要
Pythonで動いているプログラムから、C/C++で書かれたプログラムを呼び出す方法について記載する。
手法は以下の3パターン

1. Python C API
1. Cython
1. boost.python


記事が長くなってしまいそうなので、手法ごとに記事を分けて記載することにする。
今回は「**Python C API**」について記載する。

# ソースコード公開先
[GitHubのリンク](https://github.com/HIR0-728/py-to-cpp)

# Python C API

Python C APIとは、PythonモジュールをC/C++で記述可能な標準ライブラリ。
実際にPythonから呼び出す際は事前にビルドしておく必要がある。

## モジュール化したいC/C++のソースコード
まずはソースコード全体。

```C:test_c_mod.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`をインクルードする必要がある。

```C
#include <Python.h>
// パスが通ってない場合は絶対パスでもOK
#include "/usr/include/python3.8/Python.h"
```

### Pythonで実行したいC/C++の処理を実装する
まずはPythonで実行したいC/C++の関数の内容を記述する。
関数はラッパー関数を用意してもよいし、PyObject型の関数に直接処理内容を記述する方法でもよい。

```C
/* 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側に伝えるための定義を行う。

```C
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するために必要なモジュールの情報を定義する。

```C
// モジュール名の定義
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`ファイルとしてコンパイルする。

```Python:setup.py
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`ファイルが生成される。

```shell
# コンパイル実行
$ python3 setup.py build

# コンパイル実行→pipの管轄に追加
$ python3 setup.py install
$ pip freeze
```

### 実際に使用する
生成された`*.so`ファイルを参照可能なディレクトリに移動しておく。
(pipの管轄に追加した場合は不要)

```Python
import test_C_module

test_C_module.c_hello()
c= test_C_module.c_multiplication(2, 3)
print(c)

```

---

以上。
次回は、**「Cython」**を使用した方法について書く予定です。