LoginSignup
2
3

gnuplotからPythonのライブラリ関数を呼び出してゼータ関数を描画してみた(Windows環境)

Posted at

C++で作成したdllを介してPythonのライブラリ関数を呼び出す。

最終結果

image.png

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のセクションにシンボル名が吐かれるので、これで確認可能。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3