MEX関数とは?.............................................................................................................. 49
MEX関数の意義........................................................................................................... 49
簡単なMEXファイルの作成............................................................................................ 49
STEP
0: MEX ファイルをコンパイルできる環境を作る。.......................................... 49
STEP
1:もっとも簡単なMEXファイル....................................................................... 50
STEP 2:MEX ファイルの引数の数............................................................................ 50
STEP
3: MATLABへのデータの出力........................................................................... 53
STEP
4: MATLABで様々な変数の取り扱い................................................................. 54
CASE
1:doubleのスカラーのみを検出したい場合には、......................................... 54
CASE
2: double の行列を検出したい場合には、..................................................... 54
CASE
3: 文字を検出するには、............................................................................... 55
CASE
4: 文字列を検出するには、............................................................................ 55
STEP
4.1:double 行列の扱い。................................................................................... 56
STEP
4.2:文字列の扱い。............................................................................................ 56
STEP
4.3:uint8,uint16の扱い..................................................................................... 57
STEP
4.4:多次元配列................................................................................................... 58
MEXハイレベルテクニック......................................................................................... 59
演習問題........................................................................................................................... 61
MEXファイルとは、MATLAB Executable 関数の略である。MEX関数は一般にC言語やFortran言語で記述される。一般に、信号処理、データ処理、グラフィック表示などをするのであれば、MATLABスクリプトファイルのみで事は足りる。しかしなぜMEX関数という機構があるのであろうか?
MEXファイルを作る意義は、以下の2つの状況が考えられる。
1.処理速度を高速化したい
2.既存のCやFortran(ここでは扱わないが)のコードをMATLAB上で実行したい。
3.MATLABとハードウェアや外部とのゲートウェイを作りたい。
が考えられる。1の処理速度を高速化する手法として、MATLABコンパイラやRealTimeWorkshopなどが商品となっているが、同然自分でCコードを書いた方が最適化できる可能性がある。3の外部ハード、ソフトウェアとのリンクは、これ以外の方法としてDDE(ダイナミックデータエクスチェンジ)ActiveXなどWindowsのアプリケーション間コミュニケーションAPIを利用することができる。(さらにVer5.3からは、まだ実験的であるが、Javaのインターフェースも実装している。)最新のWindowsアプリケーションであるならば、大抵そのためのアプリケーション間通信機能があるのでこれらを利用するのも手である。これらはDDE,ActiveX,Javaなどのテクニックは、特別なハードウェアの知識を要せずとも外部アプリケーションとの通信が可能である。しかし、サポートされていないようなハードウェアや、開発の為に必要なゲートウェイインターフェースなど、どうしてもMEXファイルでなければ出来ない場合も存在する。
MEXファイルによる方法は、これらの方法よりハードウェアの知識やプログラミングが要求される。しかし速度アップなどボトムリミット的なシチュエーションにおいて効果を発揮する手法である。
ここでは、簡単なC言語によるMEX関数の作り方を解説する。
具体的なMEX関数の実用例はMEX関数その2で解説する。
1.処理速度の高速化
2次元行列の最大値検出の応用
Hough変換の応用
2. ハードウェアゲートウェイ
直接I/Oアクセス関数
PIOインターフェース
AD/DAインターフェース
RS232インターフェース
TCP/UDPインターフェース
Frame Grabberインターフェース
ここでは、簡単なMEXファイルサンプルを数例示しその後で外部インターフェースを作っていこう。
MEXファイルをコンパイルできるようにするには、ANSI Cがどのディレクトリにあるか指定する必要がある。ここでは、Visual C++5.0を例に説明する。まず、コマンドプロンプトで
mex –setup
と実行し、コマンドプロンプト上でいろいろ聞かれるのでデフォルトでVisual Cを選択すればよい。
Cのhello world
まずもっとの簡単なMEXファイルを作ってみよう。とりあえず、以下にMATLAB 関数で作ったMATLABスクリプト挙げる。
mhello.m
-----------------------------------------
function mhello( )
fprintf('Hello World\n');
-----------------------------------------
このmhelloをカレントディレクトリに保存し
》 mhello
と実行する。するとHello Worldと表示されるはずである。これと同様の機能をMEXファイルで実行するプログラムは、以下のようになる。
----------- hello.c ------------------------------
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
mexPrintf("Hello World\n");
return;
}
----------------------------------------------------
実行は、カレントディレクトリにファイル名hello.cで保存し
》 mex hello.c
とする。するとカレントディレクトリには、hello.dllと言うファイルが出来る。これがMEXファイルである。(Windowsが32ビット化される前までは拡張子がMEXであったためMEXファイルと言われる。現在では、DLLファイルに置き換わった。) MEXファイルは、MATLABから以下のように直接実行できる。
》 hello
Hello World
これがもっとも簡単なMEXファイルである。ここでは、helloと入力するとコマンドウィンドウにHello World と出力している。
MEXファイルでは、Cのプログラム中にデータを渡すことも出来る。
MATLABでは、MEXファイルに付けられた引数の数をCのプログラムの中で検出することが出来る。C言語のint main(int argc,char**argv)のargcに相当する関数がmexfunctionである。
C言語のmain関数では、
int main(int argc,char *argv[]);
argc:関数の右側の引数の数
argv: 関数の右側の引数へのポインタ
の意味でありDOSプロンプト上で実行する場合
プログラム名 argv[0] argv[1] argv[2] … argv[argc-1]
として扱われる。
MATLABの場合は、main関数に相当する関数はmexFunctionであり、引数の意味は、
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]);
nlhs:関数の左側の引数の数(Number of Left-Hand Side arguments)
plhs:関数の左側の引数へのポインタ(Pointer to Left-Hand Side arguments)
nrhs:関数の右側の引数の数(Number of Right-Hand Side arguments)
prhs:関数の右側の引数へのポインタ(Pointer to Right-Hand Side arguments)
となっている。C言語の場合は、関数の入力される引数の数と引数値をプログラム内で認識できる。
言語 |
C言語 |
MATLAB |
関数 |
main(int argc,char* argv[
]) |
void mexFunction(int
nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]); |
使い方のイメージ |
>test argv[0] arcv[1]
... |
》[plhs[0] plhs[1]
...]=test(prhs[0],prhs[1] ...); |
特徴 |
引数は(入力)右側のみ指定できる |
引数は、左側(出力)、右側(入力)両方とも指定可能 但し、出力の型はチェックできない。 |
MATLABのMEX関数の特徴は、入力引数の数とその引数値、その引数タイプそれに出力の数の検出できる点である。入力数、出力数、それに入力変数のタイプを検出できるので、それらのコンビネーションの違いによりそのMEX関数の振る舞いを変えることができる。この点に関しては、後でもう少し詳しく説明するが、非常に強力である反面、MATLABのMEX関数特有のそれらを検出するためのゲートウェイルーチン(ここでのゲートウェイとはMATLABとMEXファイル間のこと)が重要になる。
》 x=hello1
》 x=hello1(1,2)
》 [x,y,z]=hello1
》 [x,y]=hello1(1,2)
》 hello1
MATLAB上でMEX関数を実行したとき、MEX関数内では、呼び出された時に何個の入出力変数をとって呼び出されているのかが検出できる。MATLABでは、MEXファイルは実行時に入力の数、出力の数が検出できるのでこれらの数の違いによってその関数の振る舞いを変えることが出来る。C++言語では、同様の概念として入力変数の型や数により呼び出す関数を変えることができるオーバーロード関数があるがMATLABでは、その出力の数も検出できる点が異なる。C++のオーバーロード関数より更にフレキシブルであると言える。ただし、左側の引数(出力変数)の型については、言語の使用上代入される変数であるため、出力変数の数は検出できるがその型は検出できない。以下が、その入出力のデータ数を調べるMEXファイルである。
----------- hello1.c ------------------------------
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
mexPrintf("Hello World nlhs = %d nrhs = %d\n",nlhs,nrhs);
return;
}
----------------------------------------------------
コンパイル例
》 mex hello1.c
実行例
入力が1つの場合、nrhsが1になっている。
》 hello1(1)
Hello World nlhs = 0 nrhs = 1
入力が1つ出力が1の場合
》 y=hello1(1)
Hello World nlhs = 1 nrhs = 1
Warning: One or more output arguments not assigned during call to 'hello1'.
入力が2つの場合
》 hello1(1,3)
Hello World nlhs = 0 nrhs = 2
入力が3つの場合
》 hello1(1,3,4)
Hello World nlhs = 0 nrhs = 3
入力が3つ出力が3つの場合
》 [a,b,c]=hello1(1,3,4)
Hello World nlhs = 3 nrhs = 3
Warning: One or more output arguments not assigned during call to 'hello1'.
この場合、割り当てられた変数があるのにその値が代入されないためこのようなワーニングが出力される。当然、同様にその引数の値も表示可能である。関数の入力された数値を取り出す関数がmxGetScalarである。この関数を使うと値の取り出しが出来る。
---------------hello2.c-------------------------------------
/* Hello fucntion for MATLAB 5.x By Gerox
Programmed by Gerox 1999.July 6
*/
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
int i;
double pdata;
mexPrintf("Hello World nlhs = %d nrhs = %d\n",nlhs,nrhs);
for(i = 0;i < nrhs;i++) {
pdata = mxGetScalar(prhs[i]);
mexPrintf("%d\n",(int)pdata);
}
return;
}
----------------------------------------------------
》 mex hello2.c
》 hello2
Hello World nlhs = 0 nrhs = 0
》 hello2(2)
Hello World nlhs = 0 nrhs = 1
2
》 hello2(2,4)
Hello World nlhs = 0 nrhs = 2
2
4
以下例は、MATLABから入力した引数からCプログラム内で処理し表示(ここでは2乗の例)した例である。
----------------hello2-1.c------------------------------------
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
int i;
double* pdata;
mexPrintf("Hello World nlhs = %d nrhs = %d\n",nlhs,nrhs);
for(i = 0;i < nlhs;i++) {
plhs[i] = mxCreateDoubleMatrix(1,1,mxREAL);
pdata = mxGetPr(plhs[i]);
mexPrintf("%f\n",pdata[0]*pdata[0]);
}
return;
}
----------------------------------------------------
MATLABでデータの出力をするには、mxCreateDoubleMatrix関数などを用いMATLABに渡す変数用メモリ領域を確保して返す必要がある。C言語などでは、動的にメモリを確保した場合には、必ず開放する必要があるが(DOSなどでは、終了すれば自動的にメモリスタックを開放するようになってはいるが、それに依存するのは、マナーの悪いプログラムである。)、MATLABではその必要はないので注意すること。一応、MATLABの関数にもメモリを開放する関数mxDestroyArrayもあるが関数の返り値にする場合には、確保した変数メモリは開放する必要はない。(つまりplhsポインタに登録される変数は開放する必要はない)メモリの管理はMATLAB内部に移り処理される。以下のプログラムは、出力の引数の数によりその引数の数プラス10の値を出力するプログラムである。
-----------------hello3.c-----------------------------------
/* Hello fucntion for MATLAB 5.x By Gerox
Programmed by Gerox 1999.July 6
*/
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
int i;
double* pdata;
mexPrintf("Hello World nlhs = %d nrhs = %d\n",nlhs,nrhs);
for(i = 0;i < nlhs;i++) {
plhs[i] = mxCreateDoubleMatrix(1,1,mxREAL);
pdata = mxGetPr(plhs[i]);
pdata[0] = i+10;
}
return;
}
----------------------------------------------------
》 mex hello3.c
》 hello3
Hello World nlhs = 0 nrhs = 0
》 y=hello3
Hello World nlhs = 1 nrhs = 0
y =
10
》 [y1,y2]=hello3
Hello World nlhs = 2 nrhs = 0
y1 =
10
y2 =
11
》 [y1 y2 y3]=hello3
Hello World nlhs = 3 nrhs = 0
y1 =
10
y2 =
11
y3 =
12
これらを組み合わせると変数の入力、出力の計算ができる。
これまでの例題では、スカラー変数のみであった。しかしMATLABで扱える変数は、これ以外でも行列変数、複素数、文字列などなど様々なタイプの変数を扱うことが可能である。これら全ての例に関しては、Manual等を参照してもらいたい。ここでは、MATLABの入出力でよく使われるであろう変数タイプ、double,uint8,uint16の行列、文字列、多次元配列の例として3次元配列についてサンプル例を示しながら解説していこう。MATLABで入力された行列の大きさを知るには、mxGetN,mxGetM関数を用いる。また、出力変数としては、スカラー変数と同様にmxCreateDoubleMatrix関数を用いる。
MATLABでは、様々なタイプの入力変数が定義されているため、入力される変数がどのようなタイプであるのかチェックするしないと予想外の値がMATLAB内で処理されることになるためMATLABがクラッシュしてしまう原因となる。そこで、引数のチェック以外にも、入力変数のタイプチェックは不可避である。チェックする代表的な変数タイプとしては、
mxIsDouble( ),mxIsChar( ),mxIsInt8( ),mxIsComplex( ), mxGetNumberOfDimensions( )
等がある。これ以外にもComplex,Cell,Sparse,Structなど様々なタイプもあるがここでは扱わない。これらの関数を組み合わせるとその引数のタイプをチェックできるのであるが、
組み合わせをチェックするよりclassIDをチェックした方が比較的容易である。
以下にMATLABでありうるクラスIDを示す。
mxCHAR_CLASS,
mxDOUBLE_CLASS,
mxINT8_CLASS,
mxUINT8_CLASS,
mxINT16_CLASS,
mxUINT16_CLASS,
mxINT32_CLASS,
mxUINT32_CLASS,
この中でここの例題で良く使用するのは、mxDOUBLE_CLASS, mx_INT8_CLASS, mx_CHAR_CLASSである。このクラスIDを得る関数としてmxGetClassID関数がある。
if((mxDOUBLE_CLASS == mxGetClassID(prhs[0])) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) == 1) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is Double Scalar \n");
return;
}
このようにする。ここで、mxGetNumverOfDimenstions( ) は、多次元配列であるか否かの検出に使う。スカラーであっても多次元配列でない場合に1×1の行列として扱われるので2が返ってくる。また,mxGetN( ) * mxGetM( ) != 0 の代わりにmxIsEmpty( )を使用してもよい。
if(mxIsDouble(prhs[0]) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) != 0) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is Double Matrix\n");
return;
}
ここでは、クラスIDを得る代わりにmxIsDouble関数を使用している。
if(mxIsChar(prhs[0]) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) == 1) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is Character \n");
return;
}
if(mxIsChar(prhs[0]) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) != 0) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is String \n");
return;
}
この例では、複素数の場合や、スパース行列などの場合は扱っていない。(複素数の場合には、虚数部は無視される)また、スパース行列の場合には、Doubleにならないので問題ないとは思われる。しかしこれは、MATLAB Ver5.2で私が独自に調べたものなのでもし例外があるようであるならば知らせてもらいたい。
------------ hello9.c----------------------------------------
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
if(nrhs == 0) return;
if(mxIsDouble(prhs[0]) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) == 1) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is Double Scalar \n");
return;
}
if(mxIsDouble(prhs[0]) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) != 0) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is Double Matrix\n");
return;
}
if(mxIsChar(prhs[0]) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) == 1) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is Character \n");
return;
}
if(mxIsChar(prhs[0]) &&
(mxGetN(prhs[0]) * mxGetM(prhs[0]) != 0) &&
(mxGetNumberOfDimensions(prhs[0]) == 2)) {
mexPrintf("This is String \n");
return;
}
return;
}
----------------------------------------------------
》 mex hello9.c
》 hello9([])
》 hello9('a')
This is Character
》 hello9('ag')
This is String
》 hello9([1])
This is Double Scalar
》 hello9([1 3])
This is Double Matrix
》
例えば、doubleの行列を引数としてとりたい場合には、Doubleのみチェックすればよいが、[ ] Null行列の場合にもdoubleの配列として扱われるので、この場合には、N*Mが0でないことをチェックするとよい。
-------------- hello5.c --------------------------------------
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
int n,m,N,M,i;
double *prdata,*pldata;
if(nrhs == 0) {
mexErrMsgTxt("One input must be required.");
return;
}
N = mxGetN(prhs[0]);M=mxGetM(prhs[0]);
if((N * M != 0) && mxIsDouble(prhs[0])) {
plhs[0] = mxCreateDoubleMatrix(M,N,mxREAL);
prdata = mxGetPr(prhs[0]);
pldata = mxGetPr(plhs[0]);
for(m = 0;m < M;m++) {
for(n = 0;n < N;n++) {
pldata[M * n + m] = prdata[M * n + m];
}
}
} else mexErrMsgTxt("Input must be double Matrix");
return;
}
----------------------------------------------------
なお、MATLABでの配列の取り方は、通常のC言語の場合とそれとは異なり縦方向に進んでいく。例えば、配列が3×2の行列があるとすると
M=3,N=2で
の順序でポインタに格納されるので注意すること!(これは、MATLABが昔Fortranで書かれていたことに由来する。)
MATLABでは文字列も同様に扱うことが出来る。
----------------------------------------------------
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
int n,m,N,M,buflen,i;
char *prdata,*pldata;
if(nrhs == 0) {
mexErrMsgTxt("One input must be required.");
return;
}
N = mxGetN(prhs[0]);M=mxGetM(prhs[0]);
if((N * M != 0) && mxIsChar(prhs[0])) {
buflen = N * M + 1;
prdata = mxCalloc(buflen,sizeof(char));
pldata = mxCalloc(buflen,sizeof(char));
if(0 != mxGetString(prhs[0],prdata,buflen)) {
mexWarnMsgTxt("Not enough space. String is truncated.");
}
memcpy(pldata,prdata,buflen);
plhs[0] = mxCreateString(pldata);
mxFree(prdata); // required!!
mxFree(pldata); // better !! but not necessary
} else mexErrMsgTxt("Input must be string");
return;
}
----------------------------------------------------
この例では、入力された文字列と同じ文字列を返すプログラムである。ここで注意する点は、文字数である。文字数は、N*M+1であることに注意すること。また、文字列のメモリ領域を確保する際mxCallocを使用しているが、MATLABでは、plshに登録されるmxCallocで確保されたメモリ配列は、MATLABに制御が移るとき自動的にmxFree関数によりメモリを開放するため明示的には開放する必要はない。しかし、一時的にメモリをmxCallocで確保する場合には、明示的にmxFree関数で開放する必要がある。なお2次元文字列配列を入力してもエラーは出ないが、この例は文字アレイのみの対応であり2次元の文字列配列には対応していない。
これらは特に画像データや、ハードの制御などビットの制御などを行う際、効率的である。
ここでは、簡単なuint8配列を例に扱う。一般的には、キャストによりuint8からdoubleまたその逆も可能であるが、メモリの効率的な利用、処理速度の高速化の面からもuintタイプのデータを扱うようにした方が便利である。
------------- hello7.c ---------------------------------------
/* Hello7 fucntion for MATLAB 5.x By Gerox
Programmed by Gerox 1999.July 6
*/
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
int n,m,N,M,i;
unsigned long uint8len;
unsigned char *prdata,*pldata;
int dims[3];
if(nrhs == 0) {
mexErrMsgTxt("One input must be required.");
return;
}
N = mxGetN(prhs[0]);M=mxGetM(prhs[0]);
if((N * M != 0) && mxIsUint8(prhs[0])) {
uint8len = (unsigned long)N*M;
prdata= (unsigned char*)mxGetPr(prhs[0]);
dims[0] = M;
dims[1] = N;
dims[2] = 0;
plhs[0] = mxCreateNumericArray(2,dims,mxUINT8_CLASS,mxREAL);
pldata = (unsigned char *)mxGetPr(plhs[0]);
memcpy(pldata,prdata,uint8len);
} else mexErrMsgTxt("Input must be uint8 array");
return;
}
----------------------------------------------------
ここで使用しているmxCreateNumericArrayは、多次元配列もサポートしている。最初のパラメータを次元数を指定し、dims配列の中にここの数を入れる。先ほど説明したのと同様にC言語の場合とは異なるので配列の順序に注意する必要がある。また、mxIsUint8をmxIsUint16へmxUINT8_CLASSをmxUINT16_CLASSへ変更、memcpyのuint8lenを2倍すれば、uint16への対応も可能である。
MATLABは、多次元配列をサポートしているが、C言語の配列の保存法と異なるためわかりづらい。以下に示すスクリプトは、入力に多次元行列を入力したときどのような順序で出力するのか調べるプログラムである。
------------- hello13.c ---------------------------------------
/* Hello12 fucntion for MATLAB 5.x By Gerox
Programmed by Gerox 1999.July 6
for Multi Dimentional array demonstration program
*/
#include <stdio.h>
#include "mex.h"
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
int l,n,m,i,j,k,L;
int *pldims,*prdims;
int nldims,nrdims;
double *pldata,*prdata;
if(nrhs == 0) {
mexErrMsgTxt("One input must be required.");
return;
}
nrdims = mxGetNumberOfDimensions(prhs[0]);
if(!mxIsEmpty(prhs[0]) && mxIsDouble(prhs[0])) {
prdims = (int*)mxGetDimensions(prhs[0]);
nldims = nrdims;
pldims = (int*)mxCalloc(nldims,sizeof(int));
for(i = 0;i < nrdims;i++) pldims[i] = prdims[i];
plhs[0] = mxCreateNumericArray(nldims,pldims,mxDOUBLE_CLASS,mxREAL);
mxFree(pldims);
pldata = mxGetPr(plhs[0]);
L = mxGetNumberOfElements(prhs[0])/prdims[0] / prdims[1];
for(k = 0;k < L;k++) {
for(j = 0;j < prdims[0];j++) {
for(i = 0;i < prdims[1];i++) {
pldata[i+prdims[0] * j+ prdims[0] * prdims[1] * k] =
i+prdims[0] * j+ prdims[0] * prdims[1] * k;
}
}
}
return;
} else mexErrMsgTxt("Input must be double Matrix");
return;
}
----------------------------------------------------
》 hello12(ones(2,2,2))
ans(:,:,1) =
0 2
1 3
ans(:,:,2) =
4 6
5 7
》 hello12(ones(2,2,2,2))
ans(:,:,1,1) =
0 2
1 3
ans(:,:,2,1) =
4 6
5 7
ans(:,:,1,2) =
8 10
9 11
ans(:,:,2,2) =
12 14
13 15
実行結果より、2x2の3次元配列の場合には、
(:,:,1)=、(:,:,2)=
さらに4次元配列の場合にも同様に
(:,:,1,1)=、(:,:,2,1)=
, (:,:,1,2)=
、(:,:,2,2)=
とMATLAB内部でのデータは、列ベースで格納されていることがわかる。
MATLAB5.2から導入されたpersistent変数やC言語のstatic 変数の様な、関数が呼び出されるとそれが始めてか2回目か?などを保持できる機構がMEXファイルでもある。例えば、RS232のインターフェースなどのプログラムを書いた場合、MATLABプロンプトから関数を呼び出す毎に毎回ポートをオープン、クローズするのでは、オーバーヘッドの面から効率が悪い。出来れば、最初に呼ばれたときのみポートをオープンし、終了するときに明示的にクローズできると便利である。実は、似たよう機能は、C++言語でも存在し、classのコンストラクタ、デストラクタと呼ばれている。変数の宣言時と終了時に実行される関数である。これと同等の機能を実現するには、以下の手順をすればよい。
キーポイントとなる手順は以下の通りである。
1) static キーワードでグローバル変数を定義する。
static int initialized = 0;
2) デストラクタ(クリーンアップ関数)を作る。
void cLeanup(void) {
if(initialized == 1) {
initialized = 0;
}
}
3) コンストラクタ関数を作る。
mexfunctionの中でもしinitalizedが0であるならば初期化を行う。この際、メモリを確保したい場合などは、mxCallocなどを用いる。またこの変数も保持したい場合には、mexMakeMemoryPersistent関数により自動的に開放しないように登録する。(開放は(2)のクリーンアップルーチンで行う。)mexAtExit(cLeanup)として登録する。
なお、mexAtExitに書かれた関数が呼び出されるには、
clear mex
clear all
pack
clear functions
などを実行した場合に呼び出されることになっている。通常のclear文では、実行されないので注意すること。
以上の手順で行ったMEXファイルを以下に示す。
-------------hello13.c---------------------------------------
/* Hello13 function for MATLAB 5.x By Gerox
Programmed by Gerox 1999.July 6
for destructor constructor demonstration program
*/
#include <stdio.h>
#include "mex.h"
static int initialized = 0;
static double *sample = NULL;
void cLeanup(void) {
if(initialized != 0) {
// do something
mexPrintf("MEX-file is safely terminated.\n");
mxFree(sample);
initialized = 0;
}
}
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {
double *pldata;
if(nrhs != 0) {
mexErrMsgTxt("zero input must be required.");return;
}
if(nlhs != 1) {
mexErrMsgTxt("one output must be required.");return;
}
if(initialized == 0) {
sample = (double*)mxCalloc(1,sizeof(double));
mexMakeMemoryPersistent(sample);
mexAtExit(cLeanup);
sample[0] = 0;
initialized++;
}
plhs[0] = mxCreateDoubleMatrix(1,2,mxREAL);
pldata = mxGetPr(plhs[0]);
pldata[0] = (double)initialized++;
sample[0] += 1.0;
pldata[1] = sample[0];
return;
}
----------------------------------------------------
MALTAB内で動作するMEX関数は、MATLAB内で1回実行すると自動的に終了しMATLABプロンプト状態に戻る。MEX関数は、実行を高速化するため、一度呼び出されるとMATLAB内部に常駐するような設定になっている。このMEXファイルは、一回実行すると、2つの変数initialized,sample[0]にその実行回数を記録し、1x2の行列として出力するプログラムである。内部でsampleのように内部でメモリを確保した場合には、mexMakeMemoryPersistent、もし行列の場合には、mexMakeArrayPersistent関数で変数をクリアしないようにMATLABに登録しておく必要がある。
》 y=hello13
y = 1 1
》 y=hello13
y = 2 2
》 clear
》 clear mex
MEX-file is safely terminated.
ここで重要なのが、終了の方法である。単にclear関数を実行しただけでは、終了するための関数は実行されない。終了するための関数(cLeanup)を実行するには、
packコマンド、clear functions,clear mex,clear all, clear関数名
を実行したときそれに、MATLABを終了させたときにコールされる。
3.1 MEX関数は、どのような時に有用かを述べよ。
3.2 MEX関数を用い、y=func2(a,b,x)としてy=ax+bを計算するCプログラムを作成せよ。ただし、a,b,xはスカラーとする。
3.3 y=func3(a,b,x)としてa,bはスカラーであると仮定し、xがベクトルで入力されてもy=ax+bが計算できるように改造せよ。
3.4 y=func4('0x012a');など16進数を2バイト分の入力し10進数の数を出力するMEX関数を作成せよ。