gnuplot 5.0以降では、C言語で書かれた関数をgnuplotで取り込んで使うための非常に強力なコマンド「import
」が使えるようになりました。ここでは、そのimport
コマンドの使い方について詳細に解説します。
例として、その1で作成したdllファイルdemo_plugin.dll
(Linuxなどでは.soファイル)が、スクリプトファイルと同じフォルダにある場合を考えます。このdllファイル内では、いくつかの関数が定義されていますが、例えばその中のnsinc
関数をgnuplotで利用できるようにするには、以下のようにします。
import nsinc(n,x) from "demo_plugin"
もし、gnuplot上で使いたい名前と、dllファイル中で使いたい名前が異なる場合は、以下のようにします。以下では、my_func(n,x)
という名前でnsinc
関数を使うことになります。
import my_func(n,x) from "demo_plugin:nsinc"
これらのコマンドでエラーが出なければ、これでnsinc(n,x)
(またはmy_func(n,x)
)という関数を、あたかもgnuplot上で定義された関数であるかのように使うことができます。例えば、以下のスクリプトで、nsinc(n,x)
をプロットできます。
import nsinc(n,x) from "demo_plugin"
plot nsinc(1,x), nsinc(2,x), nsinc(3,x), nsinc(4,x)
なお、その1や1.5でも書きましたが、gnuplotのbit数と、用いたコンパイラのbit数が整合しない場合は、
warning: dynamic library error
failed to load external function
という2つのエラーが出て、スクリプトの実行が止まってしまいます。
さて、いよいよ、gnuplotで読み込めるc言語プログラムを書いてみることにしましょう。参考にするために「demo_plugin.c」の中身を見てみると、関数定義の部分は以下のようになっていることがわかります。
DLLEXPORT struct value sinc(int nargs, struct value *arg, void *p)
{
double x = RVAL(arg[0]);
struct value r;
r.type = CMPLX;
/* Enforce a match between the number of parameters declared
* by the gnuplot import command and the number implemented here.
*/
RETURN_ERROR_IF_WRONG_NARGS(r, nargs, 1);
/* Sanity check on argument type */
RETURN_ERROR_IF_NONNUMERIC(r, arg[0]);
r.v.cmplx_val.real = sin(x)/x;
r.v.cmplx_val.imag = 0.0;
return r;
}
いくつか、この関数定義の特徴を列挙したいと思います。
DLLEXPORT struct value
で宣言します。int nargs, struct value *arg, void *p
の3つです。初めの2つは、それぞれ、(gnuplotから呼ぶときの)引数の数と、引数を格納するポインタです。(void *p
の用途はよくわかりません)struct value r
を関数の中で定義して、そこに関数の返す値を入れる必要があります。x = RVAL(arg[0]);
ようにして受け取ります。RETURN_ERROR_IF_WRONG_NARGS(r, nargs, 1);
の部分でチェックします。RETURN_ERROR_IF_NONNUMERIC(r, arg[0]);
でチェックします。r.v.cmplx_val.real = ?????;
として、r
の実数部分に答えを代入します。虚数部分r.v.cmplx_val.imag
にはゼロを代入します。以上の流れに倣うと、自分なりに新しい関数を定義するCプログラムを作ることができます。例えば、以下のような例(ファイルpow.c
とします)を見てみましょう。
#include "gnuplot_plugin.h"
#include <math.h>
/* xのy乗を計算する関数 gp_pow(x,y) を定義*/
DLLEXPORT struct value gp_pow(int nargs, struct value *arg, void *p)
{
double x = RVAL(arg[0]); /* 一番目の引数を数値に変換してxに格納 */
double y = RVAL(arg[1]); /* 二番目の引数を数値に変換してyに格納 */
struct value r; /* 返す数値を格納するために使う構造体を定義 */
r.type = CMPLX; /* 返す値の種類を複素数(実数含む)に */
RETURN_ERROR_IF_WRONG_NARGS(r, nargs, 2); /* 引数が2個であることをチェック */
RETURN_ERROR_IF_NONNUMERIC(r, arg[0]); /* 引数が数値であることをチェック */
RETURN_ERROR_IF_NONNUMERIC(r, arg[1]); /* 引数が数値であることをチェック */
r.v.cmplx_val.real = pow(x, y); /* pow(x,y)を計算して、rの実部へ代入 */
r.v.cmplx_val.imag = 0.0; /* rの虚部はゼロ */
return r; /* rを返す */
}
この例は、xのy乗を計算する関数pow(x,y)
をgnuplotで使えるようにするためのプログラムです。それぞれの行の意味はコメントに書いていますが、注意すべきなのは、関数が引数を2個取るため、最初の例からの変更(青字部分)が必要なことです。これが書けたら、MinGW-w64 Win32 Shell(またはMinGW-w32 Win32 Shell; 32bitの場合)から以下のようにコンパイルします。(その1で説明したMinGW/MSYSをつかう場合は、gcc
の部分をC:/MinGW/bin/gcc.exe
に変更します)
gcc -shared -o pow.dll pow.c -DHAVE_CONFIG_H -I.
すると、pow.dll
というファイルができます。このファイルと同じフォルダで以下のようなgnuplotスクリプトを走らせれば、pow(x,y)
関数が使えます。
import pow(x,y) from "pow:gp_pow"
set xrange [0:1]
set key left
plot pow(x,0.5), pow(x,1), pow(x,2), pow(x,3)
このスクリプトの実行結果は以下のようになり、確かにxのy乗を計算するpow(x,y)
が使えるようになっていることがわかります。
gnuplot付属の例(demo_plugin.c)には、数値を引数に取る関数しか挙げられていませんが、実は文字列を引数に取るC言語関数も作ることができます。以下の例を見てみましょう。
#include "gnuplot_plugin.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
/* 引数が文字列かどうかチェックするためのマクロ */
#define RETURN_ERROR_IF_NONSTRING(r, arg) \
if (arg.type != STRING) { \
r.type = INVALID_VALUE; \
return r; \
}
/* 文字列中に含まれる大文字を全て小文字に変換する関数 */
DLLEXPORT struct value strlower(int nargs, struct value *arg, void *p)
{
struct value r; /* 返す値を入れる構造体rを定義 */
int i;
RETURN_ERROR_IF_WRONG_NARGS(r, nargs, 1); /* 引数の数をチェック */
RETURN_ERROR_IF_NONSTRING(r, arg[0]); /* 引数が文字列かどうかチェック */
r.type = STRING; /* rを文字列に */
/* 文字列の操作 必要に応じて書き換えればよい */
r.v.string_val = arg[0].v.string_val;
for(i=0; i<strlen(r.v.string_val); ++i){
*(r.v.string_val+i)=tolower(*(r.v.string_val+i));
}
return r; /* rを返して関数終了 */
}
ここでは、c言語の関数tolower(c)
関数を用いて、文字列中に含まれる大文字を全て小文字に変換する関数strlower
を定義しています。まず、冒頭で、引数が文字列以外だったときにエラーを返すためのマクロRETURN_ERROR_IF_NONSTRING(r, arg)
を定義しています。これは、gnuplot_plugin.h
の中で定義されているRETURN_ERROR_IF_NONNUMERIC(r, arg)
を少し書き換えたものです。関数定義の中身はほとんど数値引数の場合と一緒ですが、上で定義したRETURN_ERROR_IF_NONSTRING(r, arg)
を使うところが異なります。また、返す値も文字列である場合は、r.type = STRING;
としておく必要があります。
上記のc言語ファイルをstrlower.c
として保存し、MinGW-w64 Win32 Shell(またはMinGW-w32 Win32 Shell; 32bitの場合)から
gcc -shared -o strlower.dll strlower.c -DHAVE_CONFIG_H -I.
としてコンパイルします(その1で説明したMinGW/MSYSをつかう場合は、gcc
の部分をC:/MinGW/bin/gcc.exe
に変更します)。すると、strlower.dll
というファイルができます。そして、以下のようなスクリプトでstrlower
を呼び出すと、下図のような出力がコンソールに現れ、確かに大文字を小文字に変換できていることがわかります。
import strlower(s) from "strlower"
str = "This Is Test"
print str
print strlower(str)
# 結果を見るためのダミープロット
plot x
長くなってきたので、その3へ続きます。