PythonからC言語(my.puts)を呼び出して実行する

294, 2021-07-26

目次

PythonからC言語(my.puts)を呼び出して実行する

PythonはC言語で書かれたモジュールを呼び出すことが出来ます。
この記事ではその方法を解説します。

簡単に言うと、まずC言語でモジュールを書きます。
このモジュールはPythoの規約に沿った書き方をする必要があります。
それからそのモジュールをgccなどのコンパイラでコンパイルして共有ライブラリにします。

コンパイルした共有ライブラリをPythonインタプリタからインポートして、定義したモジュールのメソッドを呼び出します。
これが一連の流れです。

C言語でモジュールを書く

C言語でモジュールを書くわけですが、今回は「my」モジュールと言うモジュールを作ります。
そして「puts」という関数をmyモジュールの中に定義します。

コードにすると↓のようになります。

/**
 * my モジュール
 * 
 * インポートすると puts 関数を使える
 * 
 * コンパイル例:
 *  gcc -I /usr/include/python3.5m -fPIC -Wall -shared -o my.so my.c
 */

// コンパイラのフラグ -I に Python.h のあるディレクトリを指定する必要がある
#include <Python.h> 

/**
 * my モジュールのメソッド
 * 引数の文字列を標準出力に出力してその長さを返す
 */
PyObject* my_puts(PyObject* self, PyObject* args)
{
    const char* name;

    // args をパースして name に値を保存する
    // "s" は文字列を表す
    // args を "s" でパースしろという命令
    if (!PyArg_ParseTuple(args, "s", &name))
        return NULL;

    long len = strlen(name);  // 文字列の長さを取得
    int result = puts(name);  // name を stdout に出力
    if (result == EOF) {  // 結果が失敗だったら
        len = result; // len に EOF(-1) を入れる
    }

    // 文字列の長さを返す
    // PyLong_FromLong は整数オブジェクトを生成する
    return PyLong_FromLong(len);
}

// モジュール内のメソッドを定義する
// puts がメソッド名で my_puts がメソッドの本体
// METH_VARARGS は引数の定義
// 一番最後にメソッドの説明
static PyMethodDef my_methods[] = {
    {"puts", my_puts, METH_VARARGS, "Print argument string to stdout"},
    {NULL},  // 番兵
};

// モジュールのドキュメント
#define my_doc \
    "My module is my module.\n"

// モジュールの定義
static struct PyModuleDef module =
{
    PyModuleDef_HEAD_INIT,
    "my",  // モジュール名。インポートするときに使用される名前
    my_doc,  // モジュールのドキュメント
    -1,  // モジュールのインタプリタのステートのサイズ
    my_methods  // モジュール
};

// モジュールの初期化
// PyInit_(モジュール名)になる
// 今回は my モジュールを作るので PyInit_my になる
PyMODINIT_FUNC PyInit_my(void)
{
    return PyModule_Create(&module);
}

モジュールをコンパイルする

先ほどのモジュール(my.c)をコンパイルするには↓のようにします。

$ gcc -I /usr/include/python3.5m -fPIC -Wall -shared -o my.so my.c

↑のように-sharedフラグを付けて共有ライブラリ(my.so)としてコンパイルします。
-Iフラグのパスは環境によって変わります。
筆者の環境では/usr/include/python3.5mディレクトリ以下にPython.hがあったので、ここを指定しています。

モジュールをインポートする

Pythonインタプリタを起動してモジュールをインポートし、puts()関数を実行します。

$ ls
my.c my.so

$ python
import my

result = my.puts("Hi")
# Hi

print(result)
# 2

以上のようにC言語で書かれたモジュールをPythonから呼び出すことが出来ました。

おわりに

Pythonではこのように簡単にC言語と連携を取ることが出来ます。
速度の必要な処理を書きたい場合はC言語を使えばいいわけですね。

便利だな~

Python様様