Scilab : Cで関数を作成する

シェアする

Scilabのループ命令があまりにも遅いので,Cで書いてコンパイルしたネイティブ・コードをscilabからリンクして利用する方法を見つけたので紹介.オリジナルは以下(英語):

http://audition.ens.fr/brette/coursneurones/ScilabMaterial/Tutorial%20Scilab%20Toolbox.pdf

動作環境は,Visual C++ Express Edition 2008, Windows Vista, Scilab 5.0.3 for win.リンク先のドキュメントではVisual C++ .net 2003を使ってるが,2008でも手順はほとんど同じ.

(1). Visual C++ を起動
(2). メニューから,「ファイル」->「新規作成」->「プロジェクト」
(3). プロジェクトの種類を「win32」,テンプレートを「Win32 プロジェクト」にする.プロジェクト名を入力(例:Example1)
(4). アプリケーションウィザードが起動するので,「次へ > 」のボタンを押す.
(5). アプリケーションの種類を,「DLL」とし,追加オプションとして「シンボルのエクスポート」をチェックし,「完了」ボタンを押す.

これでプロジェクトファイルは作成される.ここで,matcalcというScilab関数を作成してみる.これは,行数n, 列数mを入力とし,n行m列の行列bを出力する簡単なもの.

(6). Example1.h に以下を書く.

extern “C” {
EXAMPLE1_API void matcalc(int *n, int *m, int b[]);
}

(7). Example1.cpp にmatcalc の具体的な処理を書く.

EXAMPLE1_API void matcalc(int *n, int *m, int b[]) {
for(int i=0; i<(*n)*(*m); i++)
b[i] = i;
}

(8). デフォルトではDebugモードになってるが,これをReleaseモードに変更
(9). メニューから,「ビルド」->「ソリューションのビルド」でコンパイル.エラーがなければ,Example1.dll が作成される.これをScilabの作業用ディレクトリにコピー(C:\Users\ユーザ名\Documents\Visual Studio 2008\Projects\Example1\Release にdllファイルがある)
(10). Scilabを起動し,Scilab用の作業用ディレクトリに移動.
(11). 以下のScilab用スクリプトファイル(matcalc.sce)を書く.

functions=[“matcalc”;];
link(“Example1.dll”,functions, “c”);  // dllをリンク
function val = matcalc(n,m) // 関数matcalcの定義
val = call(“matcalc”, n, 1, “i”, m, 2, “i”, “out”, [m n], 3, “i”);
endfunction

(12). (11)で作成したファイルをexec 命令で実行.実行結果は以下.

–> getf matcalc.sce
–> exec matcalc(2,2)
ans =
0. 2.
1. 3.
–>

行列は2次元配列ではなく,1次元配列で扱うことになる.したがって,行列の値を受けるCのプログラムの引数は,int b[] で定義してるが,これを2次元配列である int *b[] と書いても正しく動作しないようだ.

関数 call の引数の意味は以下:

最初にdll内の関数名”matcalc”を指定
次に,引数として渡す変数名と,それが何番目の引数なのか,引数の型,という順番で3つをワンセットに書く.

例えば,n,1,”i” は,変数nを1番目の整数型引数として渡す,という感じ.”d”はdouble,”r”はfloat(FORTRANのreal),”i”はint.scilabのhelpに説明がある.

計算結果が格納されている変数は,”out”から後ろの記述.まず,変数のサイズ,何番目の引数か,変数の型という順番.[m n]はm行n列の行列という意味.

一度リンクしたdllファイルは使用中になるので,Visual C++の方で新たにリビルドしたファイルをコピーしようとしても,ファイルが使用中で上書き出来ないとのメッセージが出る.そのときは,Scilabの方でdllをアンリンクし,そのあとでコピーするとよい.アンリンクするコマンドはulink()

大きいサイズの行列を計算する場合,スタックオーバーフローになる場合がある.そのときは,スタックサイズを最大値に設定するとよい.コマンドは,stacksize(“max”).それでも解消されないときは,物理的にメモリを増やすか,大きいサイズの行列を使わなくても済むようにプログラムを工夫するしかない(と思う).