通過MATLAB將C/C++函數編譯成MEX函數,在MATLAB中就可以調用了。
1,首先裝編譯器
Matlab裏鍵入mex -setup,選擇你要編譯C++的編譯器
2,寫C++函數
函數的形式必須是
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
nlhs:輸出參數個數plhs:輸出參數列表
nrhs:輸入參數個數
prhs:輸入參數列表
,不過函數名可以隨便取的。注意:保存的文件名就是將來在MATLAB中調用的函數名,而不是這裏的函數名。
下面給出一個例子,目的是想截取數組的部分元素組成新的數組
輸入參數3個,目標數組,截取的行(向量),截取的列(向量)
輸出參數2個,截取後數組,數組維數信息
在函數中展示瞭如何傳入傳出參數,以及如果從參數列表中取出每一個參數,MATLAB數據和C++數據的互相轉換,還有一些輸出函數等。
新建一個ResizeArray.cpp文件(ResizeArray將作爲MATLAB調用的函數名),寫入下面代碼
include "mex.h"
//author: 汪幫主 2010.05.05
//MATLAB調用形式: [resizedArr, resizedDims] = ResizeArray(arr, selRows, sekCols)
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{ #
if (nrhs != 3)
{
mexErrMsgTxt("參數個數不正確!");
}
int rowNum = mxGetM(prhs[0]);
int colNum = mxGetN(prhs[0]);
double* pArr = (double*)mxGetPr(prhs[0]);
//得到選擇的行列信息
//無論是行向量還是列向量均支持
double* pSelRows = (double*)mxGetPr(prhs[1]);
double* pSelCols = (double*)mxGetPr(prhs[2]);
int selRowsRowNum = mxGetM(prhs[1]);
int selRowsColNum = mxGetN(prhs[1]);
if (selRowsRowNum!=1 && selRowsColNum!=1)
{
mexErrMsgTxt("行參數不正確!");
}
int selRowsNum = selRowsRowNum*selRowsColNum;
int selColsRowNum = mxGetM(prhs[2]);
int selColsColNum = mxGetN(prhs[2]);
if (selColsRowNum!=1 && selColsColNum!=1)
{
mexErrMsgTxt("列參數不正確!");
}
int selColsNum = selColsRowNum*selColsColNum;
plhs[1] = mxCreateDoubleMatrix(2, 1, mxREAL);
double* resizedDims = (double*)mxGetPr(plhs[1]);
resizedDims[0] = selRowsNum;
resizedDims[1] = selColsNum;
plhs[0] = mxCreateDoubleMatrix(selRowsNum, selColsNum, mxREAL);
double* pResizedArr =(double*)mxGetPr(plhs[0]);
//這裏因爲MATLAB中數據得按列優先
#define ARR(row,col) pArr[(col)*rowNum+row]
#define RARR(row,col) pResizedArr[(col)*selRowsNum+row]
for(int ri=0; ri<selRowsNum; ri++)
{
for(int ci=0; ci<selColsNum; ci++)
{
RARR(ri,ci)=ARR((int)pSelRows[ri]-1,(int)pSelCols[ci]-1);
}
}
mexPrintf("OK!\n");
}
3,編譯C++函數爲MEX函數
將ResizeArray.cpp放在MATLAB當前目錄中,在MATLAB中輸入mex ResizeArray.cpp,編譯成功後將會生成ResizeArray.mexW32
4,調用函數
arr=[11:19;21:29;31:39;41:49;51:59;61:69];
selRows=[1 3];
selCols=[2:4 5 9];
[rarr,rdims]=ResizeArray(arr,rows,cols);
arr中數據:11 12 13 14 15 16 17 18 19
21 22 23 24 25 26 27 28 29
31 32 33 34 35 36 37 38 39
41 42 43 44 45 46 47 48 49
51 52 53 54 55 56 57 58 59
61 62 63 64 65 66 67 68 69
rarr中數據:
12 13 14 15 19
32 33 34 35 39
rdims爲:
2
5
OK,done!
MATLAB程序存在的一般問題是:
1、運算速度較慢,特別是有for i=1:???
for j=1:???
......
end
end時,那你就死定了。
2、無法脫離MATLAB環境運行,雖然你可以用MATLAB自帶的工具把M文件轉成可執行程序,但是執行時,你只能看到一個黑黑的窗口,難道,這就是......Windows? $%&%^$##%&。
3、GUI功能差。許多大俠不願意看到2中所述的DOS-Windows界面,於是使用了MATLAB帶的GUI生成工具。(不要扔臭雞蛋!我知道你用Graphic Editor改了一下界面,發現以前的工作全都白做了——因爲被覆蓋了,西西)但是MATLAB提供的控件和事件實在有限。GUI界面做好後放在機器裏,向尚未進實驗室的師妹(們)炫耀一下,還勉強可以增加一些魅力值。如果被專家看到了,西西。只怕效果不太美妙。
因此,如果能夠實現MATLAB與VC或者BC或者C++BUILDER等可視化設計語言的交互,提高速度,美化界面,使程序更符合Windows的規範,同時又利用MATLAB的強大功能,對任何人來說都很有意義。
我在課題中涉及到了部分MATLAB與C++的接口知識,在這裏總結出來,算是拋磚引玉吧。其中錯誤以及偏頗的地方,尚請各位大俠以及高手多多指教,謝謝!
(一)接口種類
用C++(或者C)語言操作MATLAB,有三種途徑:
·MEX文件
在MATLAB中可調用的C或Fortran語言程序稱爲MEX文件。MATLAB可以直接把MEX文件視爲它的內建函數進行調用。MEX文件是動態鏈接的子例程,MATLAB解釋器可以自動載入並執行它。MEX文件主要有以下用途:
對於大量現有的C或者Fortran程序可以無須改寫成MATLAB專用的M文件格式而在MATLAB中執行。
對於那些MATLAB運算速度過慢的算法,可以用C或者Frotran語言編寫以提高效率。
·MAT文件應用程序
MAT文件是MATLAB專用的用於保存數據至磁盤和向MATLAB導入、從MATLAB導出數據的數據文件格式。MAT文件提供了一種簡便的機制,它允許你在兩個平臺之間以靈活的方式移動數據。而且,它還提供了一種途徑來向其它單機MATLAB應用導入或者導出數據。
爲了簡化在MATLAB環境之外對MAT文件的使用,MATLAB給出了一個操作例程庫,通過它,我們可以使用C/C++或者Fortran程序讀寫MAT文件。
·引擎應用程序
MATLAB提供了一系列的例程使得別的程序可以調用MATLAB,從而把MATLAB用作一個計算引擎。MATLAB引擎程序指的是那些通過管道(在UNIX系統中)或者ActiveX(在Windows系統中)與獨立MATLAB進程進行通信的C/C++或者Fortran程序。
MATLAB同樣提供了一個函數庫來啓動或者結束MATLAB進程、與MATLAB交換數據以及發送MATLAB命令。
(二)MEX文件
1、一個MEX的例子#include "mex.h"
/*
* timestwo.c - example found in API guide
*
* Computational function that takes a scalar and doubles it.
*
* This is a MEX-file for MATLAB.
* Copyright (c) 1984-1998 The MathWorks, Inc.
*/
/* $Revision: 1.5 $ */
/* 本MEX文件的目的是實現timestwo的功能
void timestwo(double y[], double x[])是你的C++函數 */
void timestwo(double y[], double x[])
{
y[0] = 2.0*x[0];
}
/*下面這個mexFunction的目的是使MATLAB知道如何調用這個timestwo函數*/
void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
/* nlhs是MATLAB命令行方式下輸出參數的個數;
*plhs[]是MATLAB命令行方式下的輸出參數;
nrhs是MATLAB命令行方式下輸入參數的個數;
*prhs[]是MATLAB命令行方式下的輸入參數; */
{
double *x,*y;
int mrows,ncols;
/* Check for proper number of arguments. */
if(nrhs!=1) {
mexErrMsgTxt("One input required.");
} else if(nlhs>1) {
mexErrMsgTxt("Too many output arguments");
}
/* 在MATLAB命令行方式下,本MEX文件的調用格式是y=timestwo(x)
輸入參數(x)個數=1,輸出參數(y)個數=1,所以在程序一
開始就檢查nrhs是否=1以及nlhs是否>1(因爲MATLAB有一個缺省
輸出參數ans,所以nlhs可以=0 */
輸出參數ans,所以nlhs可以=0 */
/* The input must be a noncomplex scalar double.*/
mrows = mxGetM(prhs[0]); /* 獲得輸入矩陣的行數 */
ncols = mxGetN(prhs[0]); /* 獲得輸入矩陣的列數 */
if( !mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]) ||
!(mrows==1 && ncols==1) ) {
mexErrMsgTxt("Input must be a noncomplex scalar double.");
} /* 判斷輸入矩陣是否是double類,以及它是否只包括單個元素 */
/* 爲輸出創佳一個矩陣,顯然這個矩陣也應該是1x1的 */
plhs[0] = mxCreateDoubleMatrix(mrows,ncols, mxREAL);
/* 獲得指向輸入/輸出矩陣數據的指針 */
x = mxGetPr(prhs[0]);
y = mxGetPr(plhs[0]);
/* 調用C++函數timestwo(y,x) */
timestwo(y,x);
}
把上面這個文件用MEX編譯後,MATLAB命令行下調用的實例
x = 2;
y = timestwo(x)
y =
y =
4
2、MEX文件的編程規則
(1)編制自己的C++算法程序
(2)緊跟着定義mexFunction函數,mexFunction的定義法唯一:
它只能是如下形式:
void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
其名稱和參數類型不許有任何改變,在mexFunciton函數中可以調用你剛定義好的C++程序。
3、MEX文件的編譯
MATLAB提供了專門編譯MEX文件的工具:mex,它可以把你做好的C++源程序編譯成 .mex文件供MATLAB在命令行方式下調用。調用時輸入你的C++函數名(上例中爲timestwo)。具體mex的設置和使用方法可以在MATLAB命令行方式下用help mex命令獲得。
(三)MAT文件
1、概述.MAT文件是MATLAB專用的數據存儲格式,由於MATLAB提供了一套可供MATLAB調用的API函數集,所以我們完全可以在C++中訪問.MAT文件。這樣做的意義是:你可以把計算過程留給MATLAB,而用C++對計算結果進行分析或者可視化處理。
2、接口
MATLAB提供的API函數集封裝於下面兩個標準DLL文件中:libmat.dll, libmx.dll。前者用於對MAT文件的操作,後者用於對MAT文件中矩陣的操作。他們的存放路徑爲:<MATLAB>/bin。
在<MATLAB>/extern/include中有與前面兩個DLL對應的DEF文件:
libmat.edf, libmx.dbf。 其導出函數的原型位於同一目錄下的
mat.h和matrix.h中
有了這些DLL、DEF、H文件,該怎麼用C++調用API函數就不用我多說了吧。
3、MATLAB中常用的矩陣類型
·(復)雙精度矩陣(Complex Double-Precision Matrices)
MATLAB中最常用的數據類型便是(復)雙精度、非稀疏矩陣,這些矩陣的元素都是雙精度(double)的,矩陣的尺寸爲m×n,其中m是總行數,m是總列數。矩陣數據實際存放在兩個雙精度向量中——一個向量存放的是數據的實部,另一個向量存放的是數據的虛部。指向這兩個向量的指針一般被寫做“pr” (pointer to real data,指向實數據的指針)和“pi” (pointer to imaginary data,指向虛數據的指針)”。如果一個矩陣的pi爲空的話,說明它是實雙精度矩陣。
·稀疏矩陣(Sparse Matrices)
MATLAB中稀疏矩陣的存儲格式與衆不同。如同雙精度矩陣一樣,它擁有參數pr和pi,同時它還具用三個附加的參數:nzmax,ir以及jc。
nzmax是個整型數,其值爲向量ir及pr、pi(如果存在的話)可能的最大長度。它是稀疏矩陣中不爲零的元素的個數。
ir指向一個長度爲nzmax的整型數陣列,陣列包含的是pr和pi中對應元素的行號。
jc指向一個長度爲N+1的整型數陣列(N爲矩陣的列數),其中包含的是列號信息。對於任意的j,如果0≤j≥N-1,jc[j]是第j列中第一個非零項在ir、pr(以及pi)中的序號,jc[j+1]-1是第j列最後一個非零項的序號。因此jc[N]總等於nnz——矩陣中非零項的總個數。如果nnz小於nzmax,可以繼續向矩陣中添加非零項而無需分配額外的存儲空間
。
4、主要函數舉例:
·MATFile *matOpen(const char *filename, const char * mode)——打開/創建
·MATFile *matOpen(const char *filename, const char * mode)——打開/創建一個MAT文件;
·int matClose(MATFile *pMF)——關閉一個MAT文件;
·mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity flag)
——創建一個(復)雙精度矩陣;
·mxArray *mxCreateSparse(int m, int n, int nzmax, mxComplexity flag)
——創建一個稀疏矩陣;
·mxArray *matGetNextArray(MATFile *pMF)——獲得MAT文件裏面下一個矩陣;
·const char *mxGetName(const mxArray *pa)——獲得矩陣pa的名稱;
·void mxSetName(mxArray *pa,const char *s)——爲矩陣pa設置一個名稱;
·int mxGetM(const mxArray *pa)——獲得矩陣pa的總行數;
·int mxGetN(const mxArray *pa)——獲得矩陣pa的總列數;
·double *mxGetPr(const mxArray *pa)——獲得矩陣pa的pr指針;
·int *mxGetIr(const mxArray *pa)——獲得稀疏矩陣pa的ir指針;
·int *mxGetJc(const mxArray *pa)——獲得稀疏矩陣pa的jc指針;
·int matPutArray(MATFile * pMF, const mxArray * pA)
——把矩陣pA存儲入MAT文件pMAF;
·void mxDestroyArray(mxArray *pa)——釋放矩陣pa(把它從內存中撤銷);
5、兩個例子:
·獲取一個MAT文件中第一個矩陣的信息
·獲取一個MAT文件中第一個矩陣的信息
typedef struct {
char szFileName[256];
MATFile* pMatFile;
mxArray* pArray;
char szArrayName[64];
char szErrMsg[256];
unsigned int nArrayDim[2];
bool bIsSparse;
} MATFileStruct;
int GetMATFileStruct(MATFileStruct *pMAThdr)
{
if((pMAThdr->pMatFile=matOpen(pMAThdr->szFileName,"r"))==NULL)
{ strcpy(pMAThdr->szErrMsg,"Can't open this mat file");
return(0);
}/*打開一個MAT文件*/
if((pMAThdr->pArray=matGetNextArray(pMAThdr->pMatFile))==NULL)
{ strcpy(pMAThdr->szErrMsg,"Can't get arrays");
matClose(pMAThdr->pMatFile);
return(0);
}/*獲取MAT文件中的第一個矩陣*/
}/*獲取MAT文件中的第一個矩陣*/
pMAThdr->nArrayDim[0]=mxGetM(pMAThdr->pArray); /*獲取其行數*/
pMAThdr->nArrayDim[1]=mxGetN(pMAThdr->pArray); /*獲取其列數*/
strcpy(pMAThdr->szArrayName,mxGetName(pMAThdr->pArray)); /*獲取其名稱*/
pMAThdr->bIsSparse=mxIsSparse(pMAThdr->pArray); /*判斷它是否是係數矩陣*/
mxDestroyArray(pMAThdr->pArray); /*在內存中撤銷這個矩陣*/
matClose(pMAThdr->pMatFile); /*關閉MAT文件*/
return(1);
}
·創建稀疏矩陣並賦值
int i,j,k,m,n,nzmax,*ir,*jc;
double *pr;
unsigned short *pData;
mxArray *pa; file://初始化。
m=pLCMShdr->TrueHdr.nArrayDim[0]; file://獲得原矩陣行數。
n=pLCMShdr->TrueHdr.nArrayDim[1]; file://獲得原矩陣列數。
nzmax=0;
for(i=0;i<m*n;i++)
{ if(pData[i]!=0)
nzmax++;
nzmax++;
} //計算數據中非零元個數。
if(nzmax<n)
nzmax=n;
pa=mxCreateSparse(m,n,nzmax,mxREAL); /*創建一個空的稀疏矩陣pa。*/
mxSetName(pa,pLCMShdr->TrueHdr.szArrayName); /*爲稀疏矩陣pa設置名稱。*/
pr=mxGetPr(pa); //獲得pa的pr指針。
ir=mxGetIr(pa); //獲得pa的ir指針。
jc=mxGetJc(pa); //獲得pa的jc指針。
k=0;
for(j=0;j<n;j++)
{ jc[j]=k; //jc[j]:截至到第j列非零元的個數。
for(i=0;i <m;i++){ if(pData[i]!=0) //如果第j列第i行的元素是個非零元。
{ ir[k]=i; //記錄下第k個非零元的行號。
k++;
}
}
pData+=m; //移動pData指針到下一列。
}
jc[n]=k; //jc[n]等於矩陣中非零元的個數。
matPutArray(pmat,pa); //把稀疏矩陣pa存入MAT文件pmat。
mxDestroyArray(pa); //從內存中撤銷矩陣pa。
(五)引擎應用程序
1、簡介引擎應用程序的實質是把MATLAB做爲一個引擎,它允許從你自己的C++程序調用這個引擎。在運行時,引擎作爲一個進程單獨運行,你的C++程序也作爲一個進程單獨運行,二者可以通過進程間的通信機制進行交互。
2、引擎庫
MATLAB引擎庫包含了若干個控制MATLAB引擎的函數,如下所示:
engOpen 啓動MATLAB引擎
engClose 關閉MATLAB引擎
engGetArray 從MATLAB引擎中獲取一個MATLAB矩陣
engPutArray 向MATLAB引擎發送一個MATLAB矩陣
engEvalString 執行於一個MATLAB命令
engOutputBuffer 創建一個存儲MATLAB文本輸出的緩衝區
同時,引擎應用程序還可以使用前面提到的API函數。
3、一個例子
從這個示例中,我們看出引擎應用程序是如何編制的:
/* $Revision: 1.3 $ */
/*
* engdemo.c
*
* This is a simple program that illustrates how to call the
* MATLAB engine functions from a C program.
*
* Copyright (c) 1996-1998 The MathWorks, Inc.
* All rights reserved
*/
#include
#include
#include
#include "engine.h"
#define BUFSIZE 256
int main()
{
Engine *ep;
mxArray *T = NULL, *result = NULL;
char buffer[BUFSIZE];
double time[10] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0,
8.0, 9.0 };
8.0, 9.0 };
6-6
/*
* Start the MATLAB engine locally by executing the string
* "matlab".
*
* To start the session on a remote host, use the name of
* the host as the string rather than /0
*
* For more complicated cases, use any string with whitespace,
* and that string will be executed literally to start MATLAB.
*/
if (!(ep = engOpen("/0"))) {
fprintf(stderr, "/nCan't start MATLAB engine/n");
return EXIT_FAILURE;
} /*啓動MATLAB引擎*/
/*
* PART I
*
* For the first half of this demonstration, we will send data
* to MATLAB, analyze the data, and plot the result.
*/
/*
/*
* Create a variable for our data.
*/
T = mxCreateDoubleMatrix(1, 10, mxREAL); /*創建一個矩陣*/
mxSetName(T, "T"); /*設置矩陣的名字爲“T”*/
memcpy((void *)mxGetPr(T), (void *)time, sizeof(time)); /*向矩陣“T”賦值*/
/*
* 把矩陣“T”置入MATLAB引擎
*/
engPutArray(ep, T)
/*
* Evaluate a function of time, distance = (1/2)g.*t.^2
* (g is the acceleration due to gravity).
*/
engEvalString(ep, "D = .5.*(–9.8).*T.^2;"); /*執行MATLAB
命令:D = .5.*(–9.8).*T.^2;*/
/*
* 繪製圖象.
*/
engEvalString(ep, "plot(T,D);"); /*執行MATLAB命令:繪圖*/
engEvalString(ep, "title('Position vs. Time for a falling
object');"); /*執行MATLAB命令:給圖象加標題*/
engEvalString(ep, "xlabel('Time (seconds)');"); /*執行MATLAB命令:設置X軸座標*/
engEvalString(ep, "xlabel('Time (seconds)');"); /*執行MATLAB命令:設置X軸座標*/
engEvalString(ep, "ylabel('Position (meters)');"); /*執行MATLAB命令:設置Y軸
座標*/
/*
* Use fgetc() to make sure that we pause long enough to be
* able to see the plot.
*/
printf("Hit return to continue/n/n");
fgetc(stdin);
/*
* We're done for Part I! Free memory, close MATLAB engine.
*/
printf("Done for Part I./n");
mxDestroyArray(T); /*從內存中撤銷矩陣“T”*/
engEvalString(ep, "close;"); /*關閉剛纔顯示圖象的窗口*/
/*
* PART II
*
* For the second half of this demonstration, we will request
* a MATLAB string, which should define a variable X. MATLAB
* will evaluate the string and create the variable. We
* will then recover the variable, and determine its type.
*/
*/
/*
* Use engOutputBuffer to capture MATLAB output, so we can
* echo it back.
*/
engOutputBuffer(ep, buffer, BUFSIZE); /*構建MATLAB文本輸入緩衝區*/
while (result == NULL) {
char str[BUFSIZE];
/*
* Get a string input from the user.
*/
printf("Enter a MATLAB command to evaluate. This
command should/n");
printf("create a variable X. This program will then
determine/n");
printf("what kind of variable you created./n");
printf("For example: X = 1:5/n");
printf(">> "); /*要求用戶輸入一個MATLAB命令*/
fgets(str, BUFSIZE–1, stdin); /*獲得用戶輸入*/
/*
* Evaluate input with engEvalString.
*/
engEvalString(ep, str); /*執行用戶輸入的MATLAB命令*/
engEvalString(ep, str); /*執行用戶輸入的MATLAB命令*/
/*
* Echo the output from the command. First two characters
* are always the double prompt (>>).
*/
printf("%s", buffer+2); /*顯示該MATLAB命令的執行情況*/
/*
* Get result of computation.
*/
printf("/nRetrieving X.../n");
if ((result = engGetArray(ep,"X")) == NULL) /*判斷是否可以從MATLAB
引擎中獲得矩陣“X”*/
printf("Oops! You didn't create a variable X./n/n");
else
printf("X is class %s/t/n", mxGetClassName(result)); /*顯示矩陣“X”
的類型*/
} /* while(result==NULL)*/
/*
* We're done! Free memory, close MATLAB engine and exit.
*/
printf("Done!/n");
mxDestroyArray(result); /*從內存中撤銷矩陣“T”*/
engClose(ep); /*關閉MATLAB引擎*/
return EXIT_SUCCESS; /*返回*/
}
4、引擎應用程序的編譯
對於象上例中的控制檯程序,可以在MATLAB命令行中直接使用帶-f參數的mex命令編譯。
如果在普通win32 application中使用MATLAB引擎,情況則比較複雜。在Windows中,MATLAB引擎是通過ActiveX被調用的。因此你需要先Create一個OLE Automation Sever和一個OLE Client,然後通過OLE方式調用這個MATLAB引擎。具體做法可參閱相關MATLAB隨機文檔。
5、總結
MATLAB引擎的調用與其它引擎(例如數據庫引擎)的調用很類似,其步驟是聯接/啓動引擎,然後向引擎發送命令,獲得引擎處理結果。
結束語
上面簡要介紹了MATLAB與C++的幾種接口方式,我們可以根據要求的不同採用相應的方式。
此外,MATLAB還提供了一個數學庫,由此數學庫,我們可以獲得對MATLAB內部命令更多的訪問權和更靈活的訪問方式。具體內容可參考MATLAB的相關隨機文檔。
·參考文獻
1、MATLAB隨機文檔:apiguide.pdf
2、MATLAB隨機文檔:apiref.pdf
3、MATLAB隨機文檔:c_math_ref1.pdf
·備註
mex編譯器在VC6下的設置可參閱本板19#文
Bluesky的意見:
VC程序員最好用visual matcom。VB用matrixVB mathtool在主頁上提供免費試用,快去下吧。matlab的功能可在你的VC,VB中實現,而且只需兩個dll即可發行。