C言語アプリケーションからPythonコードを実行する
まず、C言語のプログラム内でPythonインタプリタを初期化し、特定のモジュール内の関数を呼び出す手順について解説します。この手法は、C言語で記述された既存の高パフォーマンスな処理の流れの中に、Pythonの柔軟なロジックを組み込みたい場合に有効です。
1. Pythonモジュールの作成
以下の内容で math_helper.py という名前のファイルを作成します。このモジュールには、2つの数値を受け取り、累乗計算を行う関数を定義します。
# math_helper.py
def calculate_power(base, exponent):
print("Python function executed successfully.")
print(f"Base value: {base}")
print(f"Exponent value: {exponent}")
result = base ** exponent
print(f"Calculation result: {result}")
return result
2. C言語からの呼び出しコード
次に、Python APIを使用して上記のモジュールをロードし、関数を実行するCプログラムを作成します。ファイル名は invoke_python.c とします。
#include <stdio.h>
#include <stdlib.h>
#include <Python.h>
int main(void) {
// Pythonインタプリタの初期化
Py_Initialize();
if (!Py_IsInitialized()) {
fprintf(stderr, "Failed to initialize Python interpreter.\n");
return -1;
}
// モジュール検索パスにカレントディレクトリを追加
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
// オブジェクト参照用ポインタの宣言
PyObject *pModuleName = NULL;
PyObject *pModule = NULL;
PyObject *pDict = NULL;
PyObject *pFunc = NULL;
PyObject *pArgs = NULL;
PyObject *pRetVal = NULL;
// モジュール名の指定とインポート
pModuleName = PyUnicode_FromString("math_helper");
pModule = PyImport_Import(pModuleName);
if (!pModule) {
fprintf(stderr, "Failed to import 'math_helper' module.\n");
goto cleanup;
}
// モジュール辞書の取得
pDict = PyModule_GetDict(pModule);
if (!pDict) {
fprintf(stderr, "Failed to retrieve module dictionary.\n");
goto cleanup;
}
// 辞書から関数オブジェクトの取得
pFunc = PyDict_GetItemString(pDict, "calculate_power");
if (!pFunc || !PyCallable_Check(pFunc)) {
fprintf(stderr, "Function 'calculate_power' not found or not callable.\n");
goto cleanup;
}
// 引数タプルの作成 (base=5, exponent=3)
pArgs = PyTuple_New(2);
PyTuple_SetItem(pArgs, 0, PyLong_FromLong(5));
PyTuple_SetItem(pArgs, 1, PyLong_FromLong(3));
// 関数の実行
pRetVal = PyObject_CallObject(pFunc, pArgs);
if (pRetVal != NULL) {
long result = PyLong_AsLong(pRetVal);
printf("C received return value: %ld\n", result);
Py_DECREF(pRetVal);
}
cleanup:
// 参照カウントのデクリメントと終了処理
if (pArgs) Py_DECREF(pArgs);
if (pModule) Py_DECREF(pModule);
if (pModuleName) Py_DECREF(pModuleName);
Py_Finalize();
return 0;
}
3. コンパイルと実行
Pythonの開発ヘッダーファイルとライブラリを指定してコンパイルを行います。環境に合わせてパスを調整してください(ここではPython 3の構文を使用しているため、Python 3.xのライブラリを想定しています)。
gcc -I/usr/include/python3.8 invoke_python.c -o invoke_python -lpython3.8
./invoke_python
実行すると、Python側のログが出力され、その後C言語側で戻り値を受け取ったログが表示されます。
PythonからのC言語共有ライブラリの呼び出し
次に、逆にPythonスクリプトからC言語で実装された関数を呼び出す方法です。Python標準ライブラリの ctypes モジュールを使用すると、動的ロードライブラリ(.soファイル)内の関数を簡単に利用できます。これは計算量の多い処理をC言語にオフロードする際に広く利用される手法です。
1. C言語ソースコードの作成
長方形の面積を計算するシンプルな関数を実装します。ファイル名は geometry.c とします。
#include <stdio.h>
int calculate_area(int width, int height) {
int area = width * height;
printf("C Library: Calculating area of %dx%d -> %d\n", width, height, area);
return area;
}
2. 共有ライブラリのビルド
Cソースコードをコンパイルし、共有ライブラリ libgeometry.so を生成します。
gcc -o libgeometry.so -shared -fPIC geometry.c
3. Pythonからの呼び出しスクリプト
生成された共有ライブラリをロードし、関数を実行するPythonスクリプトを作成します。ファイル名は run_geometry.py とします。
import ctypes
# 共有ライブラリのロード
# カレントディレクトリのライブラリを指定
try:
geo_lib = ctypes.CDLL("./libgeometry.so")
except Exception as e:
print(f"Error loading library: {e}")
exit(1)
# 関数の引数と戻り値の型を設定(オプションだが推奨)
geo_lib.calculate_area.argtypes = [ctypes.c_int, ctypes.c_int]
geo_lib.calculate_area.restype = ctypes.c_int
# C関数の呼び出し (幅: 10, 高さ: 20)
w = 10
h = 20
result = geo_lib.calculate_area(w, h)
print(f"Python received calculated area: {result}")
4. 実行結果
Pythonスクリプトを実行すると、C言語の printf による出力と、Python側での出力結果が確認できます。
python3 run_geometry.py