C++で作成したdllを介してPythonのライブラリ関数を呼び出す。
最終結果
c++ソース
とりあえず下記でいけた。解放処理とか明示的に実装してないけどGCされるんだろうか・・・
gnuplot_my_dll.cpp
extern "C"{
#include "./gnuplot_plugin.h"
}
#include <Python.h>
static PyObject *pMpmath;
static PyObject *pZeta;
static PyObject *pArgs;
static int init_flag = 0;
static struct value calc_core(int nargs, struct value *arg, void *p)
{
double s_re;
double s_im;
struct value r;
r.type = CMPLX;
RETURN_ERROR_IF_WRONG_NARGS(r, nargs, 1);
RETURN_ERROR_IF_NONNUMERIC(r, arg[0]);
s_re = arg[0].v.cmplx_val.real;
s_im = arg[0].v.cmplx_val.imag;
if ( init_flag == 0 )
{
Py_Initialize();
pMpmath = PyImport_ImportModule("mpmath");
pZeta = PyObject_GetAttrString(pMpmath, "zeta");
pArgs = PyTuple_New(1);
init_flag = 1;
}
PyTuple_SetItem(pArgs, 0, PyComplex_FromDoubles(s_re, s_im));
PyObject *ret = PyObject_CallObject(pZeta, pArgs);
const Py_complex a = {0};
if (PyArg_Parse(ret, "D", &a))
{
r.v.cmplx_val.real = a.real;
r.v.cmplx_val.imag = a.imag;
}
else
{
// fail
r.type = INVALID_VALUE;
return r;
}
return r;
}
extern "C"
{
DLLEXPORT struct value zeta(int nargs, struct value *arg, void *p)
{
return calc_core(nargs, arg, p);
}
}
参考:
コンパイル
compile.bat
g++ -shared -o mylib_py_mpmath_zeta.dll gnuplot_my_dll.cpp -DHAVE_CONFIG_H -I. -LC:\Users\(ユーザー名)\AppData\Local\Programs\Python\Python38\libs -lpython38 -IC:\Users\(ユーザー名)\AppData\Local\Programs\Python\Python38\include
pause
描画(gnuplotの描画コマンド)
gnuplot用plotファイル
plot_zeta_on_critical_line.plt
reset
set term window
# ----A----
# カレントディレクトリをライブラリの格納先パスに設定(適宜変更)
cd "C:/Program Files/gnuplot/share"
# ライブラリ関数をインポート
import zeta(x) from "mylib_py_mpmath_zeta"
# カレントディレクトリを適当な作業フォルダに変更(適宜変更)
cd "C:/Program Files/gnuplot/"
# ----B----
# 上記A~Bの部分は gnuplotrc ファイルに入れておくと便利(※不特定多数の人が使うPCの場合はお勧めしない)
set param
set tr[0:30]
set grid
set samp 500
p real(zeta(0.5+{0,1}*t)),imag(zeta(0.5+{0,1}*t))
ハマったポイント(C++でのdll作成のシンボル名)
上記、gccでビルドしたdllはgnuplotで読み込めるのに、同じソースをg++でビルドすると読み込めない。
エクスポートしたシンボル名にプレフィックスとサフィックスが自動で付与されてしまい、名前が元の関数名から変わってしまうことが原因。(マングルというらしい)
下記で紹介されているように、export "C"{
~}
で囲ってやると抑制できる。
マングル/デマングルに関する詳細な話など:
mingw64がインストールされていれば、コマンドプロンプトで
objdump -p 作成したDLL名.dll
でダンプされる情報の、[Ordinal/Name Pointer] Table
のセクションにシンボル名が吐かれるので、これで確認可能。