本文假設讀者已經懂matlab 和C的funtion寫法以及C的指標應用
不懂的人請自己去複習….
所謂Matlab呼叫C 通常就是為了速度的提升
因為Matlab實在是跑太慢了….
像一個通訊系統裡的viterbi decoder就可以考慮用C來寫
再include到Matlab的主程式裡
作法上是這樣
我們要把一個Matlab function整個用C寫好
然後compile成一個執行檔
這個執行檔可以被matlab直接呼叫 就像在呼叫一個matlab function一樣
只要名字配合好上層的程式根本不需要動
當然要這樣做你電腦裡需要有c compiler
看要裝gcc還是visual studio
在matlab的主視窗打mex setup
你就會看到它偵測到的compiler 選擇你要的
然後你要寫.c的程式
寫好後打mex filename.c
filename通常就是function name
你會得到一個叫filename.mex***的檔案
這跟你用的系統有關
假設是32bit的Windows你會得到.mexw32 64bit的Mac會得到.mexmaci64
64bit的linux會得到.mexa64這樣
好 現在來講那個.c檔怎麼寫
既然是在port一個function
當然.c檔也要寫成一個c的function
但是這樣matlab認不出來
所以其實要寫兩個function
第一個是該function的主要功能(廢話)
第二是一個gateway function 是matlab跟c溝通用的橋樑
溝通的gateway function寫法基本上很固定
既然是一個matlab function的port
它的input和output應該都是matrix
所以gateway function就是要把這些input matrix的dimension和值抓出來
然後造一個output matrix
這些抓出來的值都要餵到主function去
方法當然是…C最愛用的…指標
help裡面會寫很多mex function的用法
不管是抓dimension、抓值、造output矩陣都有
廢話不多說
假設我們的function是y=main_func(x,h)這樣的matlab function
轉換到c的話
我們先來看gateway function怎麼寫:
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
// 這一行基本上是照抄,nrhs是input個數,prhs[]是每個input的指標
// 我們的例子有兩個input, 那nrhs就是2, prhs[0]就是x, prhs[1]就是h
// 當然nlhs就是output個數,plhs就是我們的y
{
double *x_real, *x_imag, *h_real, *h_imag, *y_real, *y_imag;
// matlab的input是double的矩陣
// 因為C沒有complex這種data type,所以要分別存real和imag
mwSize x_ant, x_sample, Nrx, Ntx, Npath, Nsample;
// 這些是要用來表示x和h的dimension,用mwSize這個datatype宣告
// 其實在大部分的系統上,這個datatype跟int是一樣的...
const mwSize *h_dim_array;
// 當input的dimension大於2D時,要抓dimension就要開一個矩陣存
if(nrhs != 2)
mexErrMsgTxt("Two inputs required.");
// 檢查是不是真的吃到兩個input
x_ant = mxGetM(prhs[0]);
x_sample = mxGetN(prhs[0]);
// 這邊是抓x的dimension,x是一個x_ant by x_sample的2D array
h_dim_array=mxGetDimensions(prhs[1]);
// 這邊是抓h的dimension,h是一個4D array所以不能用GetM和GetN
// 來抓dimension,必須這樣寫把所有dimension一次抓齊
Nrx = h_dim_array[0];
Ntx = h_dim_array[1];
Npath = h_dim_array[2];
Nsample = h_dim_array[3];
// 看出來了嘛?h就是一個Nrx by Ntx by Npath by Nsample的矩陣
x_real=(double *)mxGetPr(prhs[0]);
h_real=(double *)mxGetPr(prhs[1]);
x_imag=(double *)mxGetPi(prhs[0]);
h_imag=(double *)mxGetPi(prhs[1]);
// 這邊就是在抓x和h的值了,在這邊因為x和h都是complex double
// 所以要分別用不同的function抓
plhs[0] = mxCreateDoubleMatrix(Nrx, x_sample+max_tap_delay, mxCOMPLEX);
// 造output array,這是Nrx by x_sample+max_tap_delay 2D矩陣
// 內容一樣是complex double
y_real = mxGetPr(plhs[0]);
y_imag = mxGetPi(plhs[0]);
// 把造好的矩陣的指標抓出來
main_func(x_real, x_imag, h_real, h_imag, y_real, y_imag);
// main_func就是你主要的function功能
// 要用到的東西當然都要傳過去
return;
}
就這樣 gateway function就是這樣的結構
視你的input, output格式 有很多mx function可以使用
這個就去help查吧
像如果你要造一個N-D的array當function output就要用其他指令
main_func怎麼寫就別問了…就看你的功能啊
像我是寫:
void main_func( double *x_real, double *x_imag, double *h_real, double *h_imag,
double *y_real, double *y_imag)
{
......
return;
}
這樣 因為main_func會在gateway後面被call
所以我是把main_func寫在gateway前面
整個.c檔的架構是這樣:
#include "mex.h"
// 這個一定要include,剩下看需要要補math.h或stdio.h之類的自己加
void main_func(......)
{
.......
return;
}
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
{
.......
main_func(......);
return;
}
就這樣 存成main_func.c
然後打mex main_func.c就會編出main_func.mexw32之類的東西
只要那個mex檔在matlab的path下
你就可以在matlab裡面寫y=main_func(x,h);
而這段內容就會用C執行!
注意事項:
1. C的矩陣index是從0開始不是1喔XD
2. 如果你像我那樣寫但是你的input給的是real only,那x_imag和h_imag都會是空的
當你access到時就會segmentation fault….
建議加上if (mxIsComplex(prhs[0]))做判斷
輸入為real時生出全0的imag array
3. segmentation fault是最常見的錯誤,在matlab的語言它叫index out of dimension
這件事情發生時matlab會整個當掉 所以debug會更加困難
4. compile好的mex檔是跟你的系統的,所以win下compile好的.mex到mac上是不會動的
5. 在寫main_func時要注意,雖然x是2D,h是4D,但是他全部都會存成1xn的array
所以你要access h_real[m][n][k][i]時,假設h是Nrx by Ntx by Npath by Nsample
你要寫h_real[m+n*Nrx+k*Nrx*Ntx+i*Nrx*Ntx*Npath]
這樣也好,不然宣告指標時會頭昏…
先寫到這邊