【Python】C言語で作成した関数をPythonで実行する

C/C++
田中太郎
田中太郎

C言語で書いた関数をPython のモジュールとしてimportするよ!

はじめに

私はずっとC言語を使っていたのですが、Pythonは非常に便利ですよね!感動しました!

しかし、ずっとCで書いてきたので、Cの関数をPython で使えないか調べてみました。

詳しいことは知らずに実行だけしたいかたはこちらへ

環境と制約

・CentOS
・gcc:g++ ではできませんでした。また、c++だと上手くいきませんでした。
・python3系:python2系だとラッパーの記述が変わります。
・Python.h:ないときはsudo yum install python3-devel で入るらしいがやったことないです。

手順

手順は大まかに以下となります。

  1. C言語で書いた関数を用意する
  2. 上記の関数とPythonを結びつけるラッパーを作る
  3. コンパイルする
  4. 完成!

C 言語で書いた関数を用意する

まず、C 言語で書かれた関数を用意します。サンプル(c_file.c)を下に置いておきます。

#include <stdio.h>

int func1(int x, int y){
  return x + y;
}
 
void func2(char* s, char* t){
  printf("%s %s\n", s, t);
}

上記の関数とPythonを結びつけるラッパーを作る

ラッパーを作ります。下記にサンプルコード(wrap_file.c)を置いておきます。

// /usr/include/下にPython.h があるので探してください。
#include "/usr/include/python3.6m/Python.h" 

// c_file.c で定義した関数を使用します。
extern int func1(x,y);

// Pythonのオブジェクトを作成しています。おまじないです。
PyObject* f_func1(PyObject* self, PyObject* args)
{
// ローカル変数定義
    int x; // func1のxの型
    int y; // func1のyの型
    int result; // funct1の戻り値の型

// "ii" はx と yの型をpythonの型に変換したものです。x, yがint型なので ii、他の型は下の表を参照。
    if (!PyArg_ParseTuple(args, "ii", &x, &y))
        return NULL;

// c_file.c の関数を実行
    result = func1(x, y);

// 結果を返す。ここで、"i"はresult の型です。下の表を参照。
    return Py_BuildValue("i", result);
}

// 上記とほぼ一緒なので割愛します。
extern void func2(adrs,name);

PyObject* f_func2(PyObject* self, PyObject* args)
{
    char* adrs;
    char* name;

    if (!PyArg_ParseTuple(args, "ss", &adrs, &name))
        return NULL;
    func2(adrs,name);
    return Py_BuildValue("");
}

 
// おまじないです。{Pythonで使う名前, このファイルで定義されている名前,METH_VARARGS}
static PyMethodDef methods[] = {
    {"func1", f_func1, METH_VARARGS},
    {"func2", f_func2, METH_VARARGS},
    {NULL}
};

// おまじない。
static struct PyModuleDef module_name =
{
    PyModuleDef_HEAD_INIT,
    "module_name",  // Python でimport する名前。import module_name になる。
    "",
    -1,
    methods
};

// おまじない。
PyMODINIT_FUNC PyInit_module_name(void)  // PyInit_<Python でimportする名前。>
{
    return PyModule_Create(&module_name);  // Python でimportする名前。
}
C言語Python
inti
doubled
charc
char*s

コンパイルする

つづいてコンパイルしていきます。

c_file.c とwrap_file.c をそれぞれコンパイルして、c_file.o と wrap_file.o を作成します。

gcc -fPIC -Wall -c -o c_file.o c_file.c
gcc -fPIC -Wall -c -o wrap_file.o wrap_file.c

作成したc_file.o と wrap_file.o から、module_name.so を作成します。

gcc -fPIC -Wall -shared -o module_name.so c_file.o wrap_file.o

module_name.so をpython からimportします。

import module_name

でインポートできます。

完成

実際にPythonで実行してみましょう!

実行できました!

まとめ

C言語で作成した関数をPython で実行してみました。ラッパーを作るのが面倒に思ったと思います。

なので、ラッパを自動で作成するスクリプトを作成しました。

簡単にできるのでぜひ使ってみてください。

補足資料

gcc

通常、gcc main.c をすると、

-> プリプロセッサ:コンパイルの準備。#define とかを処理する。

-> コンパイル:オブジェクトファイルに変換される。.o ファイル

-> リンク:ほかにもコンパイルされているファイルがあれば連結する。

-> 実行ファイル:a.out いつもの ./a.out するやつ。

という順番で実行ファイルが作成されます。

gcc のオプション

-fPIC

Position Independent Code でコンパイルする。

-Wall

Warning all:警告をすべて有効化する

-o <ファイル名>

<ファイル名>のファイルができる。つけないとデフォルトのa.out になる。

-c

オブジェクトファイルを作る。.o のファイル。

gcc ではなくg++でコンパイルしてみる

g++ でコンパイルすると

error: invalid conversion from ‘PyObject* ()(PyObject, PyObject, PyObject)…

というエラーが出ます。

コンバージョンできないらしいですが、詳しくはわかりません。

gcc でコンパイルしましょう。

その他

Python.h が見つからないとき

sudo find / -name “Python.h”

でPython.h が見つかります。なかったら

sudo yum install python3-devel

でPython.hが入るらしいですが、よくわかりません。

コメント

タイトルとURLをコピーしました