LoginSignup
2
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-09-08

概要

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

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

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

ソースコード公開先

GitHubのリンク

Python C API

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

モジュール化したい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をインクルードする必要がある。

#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ファイルとしてコンパイルする。

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ファイルが生成される。

# コンパイル実行
$ 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」**を使用した方法について書く予定です。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2