久しぶりの「C言語からPythonの関数呼び出し」記事として,WinPythonとVisual Studio 2017でPython embeddable用の実行ファイルを作るということを再検討していた.
まず,Python側のコードの例は以下のようにした.
今回もlistを引数としてPythonに渡して,listを返り値としてもらうという例になっている.
# -*- coding: utf-8 -*- def SampleFunc1(t, f): output = [10, 9, 7, 5, 3, 1]; return output
C(C++)言語側のコードは以下のようにした.
#include <stdio.h> #include <windows.h> #define _USE_MATH_DEFINES #include <cmath> #ifdef _DEBUG #undef _DEBUG #include <Python.h> #define _DEBUG #else #include <Python.h> #endif int main() { char Path[MAX_PATH + 1]; char Dir[MAX_PATH + 1 + 32]; char drive[MAX_PATH + 1] , dir[MAX_PATH + 1] , fname[MAX_PATH + 1] , ext[MAX_PATH + 1]; if (0 != GetModuleFileNameA(NULL, Path, MAX_PATH)) { _splitpath_s(Path, drive, dir, fname, ext); int j = 0; for (int i = 0; i < MAX_PATH + 1; i++) { Dir[j] = dir[i]; j++; if (dir[i] == '\\') { Dir[j] = dir[i]; j++; } else if (dir[i] == '\0') { break; } } sprintf_s(dir, "%s%s", drive, Dir); printf("ディレクトリ パス : %s\n", dir); } char buf1[2000]; sprintf_s(buf1, "import sys\r\nsys.path.append('%s')\r\nprint(sys.path)\r\n", dir); // 実行ファイルと同じフォルダにあるpyモジュールも使いたいので,前回より少し手を加えた. PyObject *pName, *pModule, *pFunc; PyObject *pArgs, *pArg, *pArg2, *pValue, *pValue2; Py_Initialize(); PyRun_SimpleString(buf1); pName = PyUnicode_DecodeFSDefault("SampleFunc"); pModule = PyImport_Import(pName); Py_DECREF(pName); if (pModule != NULL) { pFunc = PyObject_GetAttrString(pModule, "SampleFunc1"); if (pFunc && PyCallable_Check(pFunc)) { pArgs = PyTuple_New(2); // 関数fncの引数の数を引数にする pArg = PyList_New(1000); pArg2 = PyList_New(1000); for (int i = 0; i < 1000; i++) { double value = sin(2.0 * M_PI * 0.01 * 1.0 * i); pValue = PyFloat_FromDouble(value); pValue2 = PyFloat_FromDouble(2.0); if (!pValue) { Py_DECREF(pArgs); Py_DECREF(pArg); Py_DECREF(pArg2); Py_DECREF(pModule); return 1; } PyList_SetItem(pArg, i, pValue); PyList_SetItem(pArg2, i, pValue2); } if (PyTuple_SetItem(pArgs, 0, pArg2) != 0) return 1; if (PyTuple_SetItem(pArgs, 1, pArg) != 0) return 1; pValue = PyObject_CallObject(pFunc, pArgs); Py_DECREF(pArgs); if (pValue != NULL) { //printf("%d\n", PyList_CheckExact(pValue)); Py_ssize_t len = PyList_Size(pValue); for (int i = 0; i < len; i++) { //printf("%I32d ", PyFloat_Check(PyList_GetItem(pValue, i))); printf("%.4g\n", PyFloat_AsDouble(PyList_GetItem(pValue, i))); } //printf("return: %s\n", (char*)PyUnicode_DATA(pValue)); Py_DECREF(pValue); } else { Py_DECREF(pFunc); Py_DECREF(pModule); PyErr_Print(); fprintf(stderr, "Call failed\n"); return 1; } } else { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find function\n"); } Py_XDECREF(pFunc); Py_DECREF(pModule); } else { PyErr_Print(); fprintf(stderr, "Failed to load module\n"); return 1; } if (Py_FinalizeEx() < 0) { return 120; } return 0; }
プロジェクトのプロパティを次のように設定しておくとF5キーでデバッグがかかるようになる.
(Python部分はデバッグできない) 「ビルド後のイベント」のコマンドライン
copy .\*.py $(TargetDir)
「デバッグ」の「環境」にPython Embeddableのインストールパスを設定する.例えば
PATH="$(Path)C:\python-3.7.2.post1-embed-amd64\;"
なお,[C/C++]の[追加インクルードディレクトリ]にはWinPythonのインストールパスを設定する.例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加のライブラリディレクトリ]には 例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加の依存ファイル]には
python3.lib
を設定する.
なお途中にでている「手を加えた」としている部分は次の記事ですこし考え直した.
タグ:Python