先に作っていたサンプルでは文字列を返す例しか示していないので,Visual Studio Express 2017 for Windows Desktopで作るアプリ(Visual C++プロジェクトで作るアプリ)からPythonの関数を呼び出すという例をさらに作成していた。ただし,今回はWinPython-64bit-3.7.0.1Qt5の場合である。
詳しい扱い方,ビルド方法と実行の方法は前の記事を参照してほしい。
というのは,今回もWinPythonなのでPYTHONHOMEは必要である。もっとも,作ったexeファイルをPythonのフォルダで実行する場合にはその限りではないようだ。
サンプルコードPython側
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
def sample2(t, f):
f = np.array(f)
freq = np.linspace(0, 1.0 / (f[1] - f[0]), len(f))
F = np.fft.fft(f) / len(f) * 2
F[0] = F[0] / 2.0
# 振幅スペクトルを計算
Amp = np.abs(F)
# plot
plt.figure()
plt.subplots_adjust(wspace=0.4, \
hspace=0.0)
plt.subplot(121)
plt.plot(t, f, label='Raw signal')
plt.xlabel("time")
plt.ylabel("signal")
plt.grid()
plt.ylim([-7, 7])
leg = plt.legend(loc=1)
plt.subplot(122)
plt.plot(freq[0:int(len(F)/2)], \
Amp[0:int(len(F)/2)], \
label='Amplitude')
plt.xlabel('frequency')
plt.ylabel('amplitude')
plt.grid()
plt.ylim([0, 2])
leg = plt.legend(loc=1)
plt.show()
return Amp.tolist()
次にC側のサンプルコードを示す。
#define _USE_MATH_DEFINES
#include <cmath>
#ifdef _DEBUG
#undef _DEBUG
#include <Python.h>
#define _DEBUG
#else
#include <Python.h>
#endif
int main()
{
PyObject *pName, *pModule, *pFunc;
PyObject *pArgs, *pArg, *pArg2, *pValue, *pValue2;
Py_Initialize();
pName = PyUnicode_DecodeFSDefault("fft_test");
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, "sample2");
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(0.01 * i);
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("%I64d ", 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;
}
PyListやPyFloatなどはPython標準なのでPython.hをインクルードするだけで使える。もちろんNumpyなどの主要な拡張モジュールもヘッダファイルを提供していることがある(今回は面倒だから使っていない)。
ちなみに上の例ではPyFloatのリストとして扱っているが,もしPython側の変数Fを返したらPyComplexのリストとして扱うことが必要である。
ちなみにPy_DECREFなどはガーベージコレクションのために呼んでいるようだ。C言語では明示的に開放しないといけないが,Python側はそうなっていないためその調整のため,参照がなくなったかどうか(捨ててもよい領域かどうか)を判断するために使うらしい。