天天看點

編寫 Matlab mexFunction (C mex)資料一 MATLAB的MEX檔案編寫和調試 1. MEX的編寫格式 2. VC中編寫MEX 3. VC中調試MEX 資料二 深入第三部分 MEX函數1 MEX檔案的組成與參數2 常用的mex-函數和mx-函數

寫MEX程式其實就是寫一個DLL程式,是以你可以使用C,C++,Fortran等多種程式設計語言來寫。

編寫MEX程式的編輯器可以使用MATLAB的代碼編輯器,也可使用自己的C++編輯器,如VS2008等。

用MATLAB的編輯器的好處是,MEX函數會加粗高亮顯示,這給程式編寫帶來便利,可惜無法動态調試。如用VC即可編譯也可調試,比較友善。mex的編譯結果實際上就是一個帶輸出函數mexFunction 的dll檔案,是以會用VC編寫和調試dll,就會用VC編寫和調試MEX程式。

#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )

{

}

四個參數分别用來輸出和輸入資料: nlhs 輸出參數個數,plhs 輸出參數指針 (nrhs和prhs是輸入參數相關的)。

注意: 我們對輸出和輸入參數的操作都是通過指針的方式進行的。(這點很容易了解,因為我們的計算結果是需要傳遞給MATLAB的,實際上我們傳遞的不是資料,而是指針。MATLAB可以通過這些指針,通路記憶體中的資料。)

對輸入資料進行操作,需要通過MEX函數mxGetPr 得到資料的指針位址。 mxGetM 和 mxGetN 得到矩陣資料的行和列 (傳回整數)。對于實矩陣,我們可以定義 double *M; 來對實矩陣資料操作(不過似乎是,plhs, prhs都是指向double類型的指針,是以下面的這個M等,都要定義成double*類型的)。如:

double *M;

int m, n;

M = mxGetPr(prhs[0]); // 指針指向第一個參數的資料位址

m = mxGetM(prhs[0]);

n = mxGetN(prhs[0]);

需要注意的是,MATLAB矩陣資料的存儲順序是"從上到下,從左到右"。也就是說對于MATLAB的m x n的矩陣A。 A(1,1) 就是 *M,A(2,1) 就是 *(M+1) ,以此類推,A(i, j) 就是 *(M + m*(j-1) + (i-1)).

注意: MATLAB的名額從1開始,C的名額從0開始。

對于輸出資料,我們需要首先配置設定記憶體空間,有專門的mex函數可以使用,如:

plhs[0] = mxCreateDoubleMatrix(m, n, mxREAL); //生成m x n 的實矩陣。

同輸入資料一樣,要對輸出資料操作,我們也需要一個指向資料的指針變量,如

double *A;

A = mxGetPr( plhs[0]);

下面介紹一下如何使用VS2008編寫MEX并編譯調試。

打開Visual Studio 2008/2010/2012/2013, 建立項目, 選擇MFC DLL.

編寫 Matlab mexFunction (C mex)資料一 MATLAB的MEX檔案編寫和調試 1. MEX的編寫格式 2. VC中編寫MEX 3. VC中調試MEX 資料二 深入第三部分 MEX函數1 MEX檔案的組成與參數2 常用的mex-函數和mx-函數

打開項目屬性配置頁,選擇配置屬性目錄,然後分别進行如下操作

VC++目錄 -> 包含目錄    加入MATLAB安裝目錄下的 \extern\include 路徑。

VC++目錄 -> 庫目錄       加入MATLAB的 \extern\lib\win32\microsoft 路徑。

連接配接器 -> 輸入 -> 附加依賴項   輸入libmx.lib libeng.lib libmat.lib libmex.lib

在項目源檔案的. def 中EXPORTS段加入mexFunction, 如:

EXPORTS ; 此處可以是顯式導出 mexFunction

項目檔案中建立一個C++檔案 如 mexproc.cpp,裡面按前面介紹的格式編寫代碼即可。

像編譯其他程式那樣直接編譯即可,成功會生成dll檔案。如果編譯連結時出錯,根據錯誤提示,檢查一下lib和h的路徑是否正确,有無缺少lib檔案,代碼是否有文法錯誤等。

要調試MEX程式就要先編譯,再調用它。是以我們需要在MATLAB中調用這個函數,并在VC的MEX程式相應位置處下斷點即可。調用的函數名就是dll的主檔案名,你可以根據自己的需要改名。

我們用mymexfun.dll為例,先在VC的 mexFunction 函數代碼段開始處F9下斷。然後Ctrl+Alt+P附加MATLAB.exe程序。這樣就可以運作指令調試程式了。我們可以在MATLAB的指令行裡輸入指令:

          [輸出變量] = mymexfun(輸入變量)

程式一旦被調用,就會被斷在我們的斷點處。接着你就可以像調試C++程式那樣調試MEX程式了。(如果指令找不到,檢查一下matlab目前路徑,和path路徑。)

在MATLAB中編譯MEX可以輸入: mex 檔案名.cpp

MATLAB上編譯MEX時,你可以選擇不同的編譯器如lc, gcc等。也可以在編譯時附加lib和h檔案。關于mex的指令詳解請參考MATLAB幫助文檔。

在使用MATLAB編譯C/C++代碼時,C/C++代碼中要使用一個mexFunction函數,那麼這個函數是如何定義,在編譯時又是如何實作的呢?下面我将使用執行個體進行說明。

如一個簡單的函數:

double add(double x, double y)

    return x + y;

 }

 mexFunction的定義為:

 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])

 {

可以看到,mexFunction是沒傳回值的,它不是通過傳回值把結果傳回Matlab的,而是通過對參數plhs的指派。mexFunction的四個參數皆是說明Matlab調用MEX檔案時的具體資訊,如這樣調用函數時:

>> b = 1.1; c = 2.2;

>> a = add(b, c)

mexFunction四個參數的意思為:

nlhs = 1,說明調用語句左手面(lhs-left hand side)有一個變量,即a。

nrhs = 2,說明調用語句右手面(rhs-right hand side)有兩個自變量,即b和c。

plhs是一個數組,其内容為指針,該指針指向資料類型mxArray。因為現在左手面隻有一個變量,即該數組隻有一個指針,plhs[0]指向的結果會指派給a。

prhs和plhs類似,因為右手面有兩個自變量,即該數組有兩個指針,prhs[0]指向了b,prhs[1]指向了c。要注意prhs是const的指針數組,即不能改變其指向内容。

  因為Matlab最基本的單元為array,無論是什麼類型也好,如有double array、 cell array、 struct array……是以a,b,c都是array,b = 1.1便是一個1x1的double array。而在C語言中,Matlab的array使用mxArray類型來表示。是以就不難明白為什麼plhs和prhs都是指向mxArray類型的指針數組。

完整的add.c如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<code>#include </code><code>"mex.h"</code><code>//使用</code><code>mex</code><code>檔案必須包含頭檔案</code>

<code>//執行具體工作的C函數</code>

<code>double</code> <code>add(</code><code>double</code> <code>x, </code><code>double</code> <code>y)</code>

<code>{</code>

<code>    </code><code>return</code> <code>x+y;</code>

<code>}</code>

<code>//MEX檔案接口函數</code>

<code>void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[])</code>

<code>    </code><code>double</code> <code>*a;</code>

<code>    </code><code>double</code> <code>b,c;</code>

<code>    </code><code>plhs[0]=mxCreateDoubleMatrix(1,1,mxREAL);</code>

<code>    </code><code>a=mxGetPr(plhs[0]);//得到第一個接收輸出變量的位址</code>

<code>    </code><code>b=*(mxGetPr(prhs[0]));</code>

<code>    </code><code>c=*(mxGetPr(prhs[1]));</code>

<code>    </code><code>*a=add(b,c);</code>

mexFunction的内容是什麼意思呢?我們知道,如果這樣調用函數時:

&gt;&gt; output = add(1.1, 2.2);

  在未涉及具體的計算時,output的值是未知的,是未指派的。是以在具體的程式中,我們建立一個1x1的實double矩陣(使用 mxCreateDoubleMatrix函數,其傳回指向剛建立的mxArray的指針),然後令plhs[0]指向它。接着令指針a指向plhs [0]所指向的mxArray的第一個元素(使用mxGetPr函數,傳回指向mxArray的首元素的指針)。同樣地,我們把prhs[0]和prhs [1]所指向的元素(即1.1和2.2)取出來賦給b和c。于是我們可以把b和c作自變量傳給函數add,得出給果賦給指針a所指向的mxArray中的元素。因為a是指向plhs[0]所指向的mxArray的元素,是以最後作輸出時,plhs[0]所指向的mxArray指派給output,則 output便是已計算好的結果了。

  實際上mexFunction是沒有這麼簡單的,我們要對使用者的輸入自變量的個數和類型進行測試,以確定輸入正确。如在add函數的例子中,使用者輸入char array便是一種錯誤了。

  從上面的講述中我們總結出,MEX檔案實作了一種接口,把C語言中的計算結果适當地傳回給Matlab罷了。當我們已經有用C編寫的大型程式時,大可不必在 Matlab裡重寫,隻寫個接口,做成MEX檔案就成了。另外,在Matlab程式中的部份計算瓶頸(如循環),可通過MEX檔案用C語言實作,以提高計算速度。

一個簡單的MEX檔案例子:用m檔案建立一個1000×1000的Hilbert矩陣。

<code>% mextest.m&lt;br data-filtered="filtered"&gt;</code>

<code>tic</code>

<code>m=1000;</code>

<code>n=1000;</code>

<code>a=</code><code>zeros</code><code>(m,n);</code>

<code>for</code> <code>i</code><code>=1:1000</code>

<code>     </code><code>for</code> <code>j</code><code>=1:1000</code>

<code>         </code><code>a(</code><code>i</code><code>,</code><code>j</code><code>)=1/(</code><code>i</code><code>+</code><code>j</code><code>);</code>

<code>     </code><code>end</code>

<code>end</code>

<code>toc</code>

 在matlab中建立一個Matlab_1.cpp 檔案并輸入以下程式:

<a></a>

 該程式是一個C語言程式,它也實作了建立Hilbert矩陣的功能。在MATLAB指令視窗輸入以下指令:mex Matlab_1.cpp,即可編譯成功。進入該檔案夾,會發現多了一個檔案:Matlab_1.mexw32,其中Matlab_1.mexw32即是MEX檔案。運作下面程式:

<code>a=Matlab_1(1000);</code>

由上面實驗看出,同樣功能的MEX檔案比m檔案快得多。

MEX檔案的組成與參數

MEX檔案的源代碼一般由兩部分組成:

(1)計算過程。該過程包含了MEX檔案實作計算功能的代碼,是标準的C語言子程式。

(2)入口過程。該過程提供計算過程與MATLAB之間的接口,以入口函數mxFunction實作。在該過程中,通常所做的工作是檢測輸入、輸出參數個數和類型的正确性,然後利用mx-函數得到MATLAB傳遞過來的變量(比如矩陣的維數、向量的位址等),傳遞給計算過程。

MEX檔案的計算過程和入口過程也可以合并在一起。但不管那種情況,都要包含#include "mex.h",以保證入口點和接口過程的正确聲明。注意,入口過程的名稱必須是mexFunction,并且包含四個參數,即:

void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])

其中,參數nlhs和nrhs表示MATLAB在調用該MEX檔案時等式左端和右端變量的個數,例如在MATLAB指令視窗中輸入以下指令:

[a,b,c]=Matlab_1(d,e,f,g)

則nlhs為3,nrhs為4。

MATLAB在調用MEX檔案時,輸入和輸出參數儲存在兩個mxArray*類型的指針數組中,分别為prhs[]和plhs[]。prhs[0]表示第一個輸入參數,prhs[1]表示第二個輸入參數,…,以此類推。如上例中,d→prhs[0],e→prhs[1],f→prhs[2],f→prhs[3]。同時注意,這些參數的類型都是mxArray *。

接口過程要把參數傳遞給計算過程,還需要從prhs中讀出矩陣的資訊,這就要用到下面的mx-函數和mex-函數。

在MATLAB6.5版本中,提供的mx-函數有106個,mex-函數有38個,下面我們僅介紹常用的函數。

該函數是C MEX檔案的入口函數,它的格式是固定的:

說明:MATLAB函數的調用方式一般為:[a,b,c,…]=被調用函數名稱(d,e,f,…),nlhs儲存了等号左端輸出參數的個數,指針數組plhs具體儲存了等号左端各參數的位址,注意在plhs各元素針向的mxArray記憶體未配置設定,需在接口過程中配置設定記憶體;prhs儲存了等号右端輸入參數的個數,指針數組prhs具體儲存了等号右端各參數的位址,注意MATLAB在調用該MEX檔案時,各輸入參數已存在,是以在接口過程中不需要再為這些參數配置設定記憶體。

兩函數的具體格式如下:

void mexErrMsgTxt(const char *error_msg);

void mexWarnMsgTxt(const char *warning_msg);

其中error_msg包含了要顯示錯誤資訊,warning_msg包含要顯示的警告資訊。兩函數的差別在于mexErrMsgTxt顯示出錯資訊後即傳回到MATLAB,而mexWarnMsgTxt顯示警告資訊後繼續執行。

兩函數具體格式如下:

int mexCallMATLAB(int nlhs, mxArray *plhs[],

int nrhs, mxArray *prhs[], const char *command_name);

int mexEvalString(const char *command);

mexCallMATLAB前四個參數的含義與mexFunction的參數相同,command_name可以MATLAB内建函數名、使用者自定義函數、M檔案或MEX檔案名構成的字元串,也可以MATLAB合法的運算符。

mexEvalString用來操作MATLAB空間已存在的變量,它不傳回任何參數。

mexCallMATLAB與mexEvalString差異較大,請看下面的例子。

【例2】試用MEX檔案求5階完全圖鄰接矩陣 的特征值及對應的特征向量。

5階完全圖的鄰接矩陣為:(這裡找不到圖檔了,抱歉。不過不會影響您對本文的了解。)

下面是求該矩陣的MEX檔案。

Matlab_2.cpp

在MATLAB指令視窗輸入以下指令:

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

<code>&gt;&gt; </code><code>mex</code> <code>Matlab_2.cpp</code>

<code>&gt;&gt; </code><code>clear</code>

<code>&gt;&gt; a=</code><code>magic</code><code>(5)</code>

<code>a =</code>

<code>     </code><code>17     24      1      8     15</code>

<code>     </code><code>23      5      7     14     16</code>

<code>      </code><code>4      6     13     20     22</code>

<code>     </code><code>10     12     19     21      3</code>

<code>     </code><code>11     18     25      2      9</code>

<code>&gt;&gt; [y,z,w]=Matlab_2(5)</code>

<code>??? Undefined </code><code>function</code> <code>or</code> <code>variable </code><code>'y'</code><code>.</code>

<code>     </code><code>34     48      2     16     30</code>

<code>     </code><code>46     10     14     28     32</code>

<code>      </code><code>8     12     26     40     44</code>

<code>     </code><code>20     24     38     42      6</code>

<code>     </code><code>22     36     50      4     18</code>

<code>y =</code>

<code>      </code><code>0      1      1      1      1</code>

<code>      </code><code>1      0      1      1      1</code>

<code>      </code><code>1      1      0      1      1</code>

<code>      </code><code>1      1      1      0      1</code>

<code>      </code><code>1      1      1      1      0</code>

<code>z =</code>

<code>     </code><code>0.8333    -0.1667    -0.1667     0.2236     0.4472</code>

<code>    </code><code>-0.1667     0.8333    -0.1667     0.2236     0.4472</code>

<code>    </code><code>-0.1667    -0.1667     0.8333     0.2236     0.4472</code>

<code>    </code><code>-0.5000    -0.5000    -0.5000     0.2236     0.4472</code>

<code>    </code><code>0          0           0          -0.8944    0.4472</code>

<code>w =</code>

<code>     </code><code>-1      0      0      0      0</code>

<code>      </code><code>0     -1      0      0      0</code>

<code>      </code><code>0      0     -1      0      0</code>

<code>      </code><code>0      0      0     -1      0</code>

<code>      </code><code>0      0      0      0      4</code>

 由上面可以看出,K5的特征值為–1和4,其中–1是四重根。MATLAB提供了mexGetVariable、mexPutVariable函數,以實作MEX空間與其它空間交換資料的任務,具體可以參看MATLAB幫助文檔。

其格式具體如下:

#include "matrix.h"

mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag);

其中m代表行數,n代表列數,ComplexFlag可取值mxREAL 或mxCOMPLEX。如果建立的矩陣需要虛部,選擇mxCOMPLEX,否則選用mxREAL。

 類似的函數有:

mxCreateCellArray

建立n維元胞mxArray

mxCreateCellMatrix

建立二維元胞mxArray

mxCreateCharArray

建立n維字元串mxArray

mxCreateCharMatrixFromStrings

建立二維字元串mxArray

mxCreateDoubleMatrix

建立二維雙精度浮點mxArray

mxCreateDoubleScalar

建立指定值的二維精度浮點mxArray

mxCreateLogicalArray

建立n維邏輯mxArray,初值為false

mxCreateLogicalMatrix

建立二維邏輯mxArray,初值為false

mxCreateLogicalScalar

建立指定值的二維邏輯mxArray

mxCreateNumericArray

建立n維數值mxArray

mxCreateNumericMatrix

建立二維數值mxArray,初值為0

mxCreateScalarDouble

建立指定值的雙精度mxArray

MxCreateSparse

建立二維稀疏mxArray

mxCreateSparseLogicalMatrix

建立二維稀疏邏輯mxArray

MxCreateString

建立指定字元串的1 n的串mxArray

mxCreateStructArray

建立n維架構mxArray

mxCreateStructMatrix

建立二維架構mxArray

其格式如下:

int mxGetM(const mxArray *array_ptr);

int mxGetN(const mxArray *array_ptr);

與之相關的還有:

mxSetM:設定矩陣的行維

mxSetN:設定矩陣的列維

double *mxGetPr(const mxArray *array_ptr);

double *mxGetPi(const mxArray *array_ptr);

與之相關的函數還有:

mxSetPr:設定矩陣的實部

mxSetPi:設定矩陣的虛部

【例3】實作字元串的倒序輸出。

 這個程式中需要注意的地方是mxCalloc函數,它代替了标準C程式中的calloc函數用于動态配置設定記憶體,而mxCalloc函數采用的是MATLAB的記憶體管理機制,并将所有申請的記憶體初始化為0,是以凡是C代碼需要使用calloc函數的地方,對應的Mex檔案應該使用mxCalloc函數。同樣,凡是C代碼需要使用realloc函數的地方,對應的Mex檔案應該使用mxRealloc函數。

在MATLAB指令視窗中對revord.cpp程式代碼編譯連結:

&gt;&gt; mex revord.cpp

在MATLAB指令視窗中對C-MEX檔案revord.dll進行測試:

&gt;&gt; x='I am student.';

&gt;&gt; revord(x)

ans =

.tneduts ma I

[原作者贈言] 終于寫完了,相信大家對mex檔案應該有點熟悉了,具體還要到實際應用中慢慢體會。

沒有整理與歸納的知識,一文不值!高度概括與梳理的知識,才是自己真正的知識與技能。 永遠不要讓自己的自由、好奇、充滿創造力的想法被現實的架構所束縛,讓創造力自由成長吧! 多花時間,關心他(她)人,正如别人所關心你的。理想的騰飛與實作,沒有别人的支援與幫助,是萬萬不能的。

    本文轉自wenglabs部落格園部落格,原文連結:http://www.cnblogs.com/arxive/p/4687766.html,如需轉載請自行聯系原作者

繼續閱讀