Matlab與C/C++混合編程 Matlab調用C函數

Matlab與C/C++混合編程有不少種方式,分別適用於不一樣的狀況。編程

  1. 程序主體用Matlab編寫,有一些特別耗時的函數用C/C++改寫來提升效率,或者已經有現成的C/C++函數,應用到Matlab程序中(本文屬於這種狀況)
  2. 程序主體用C/C++編寫,部分程序想調用Matlab函數減小開發時間,本文不涉及這種狀況,建議讀者自行查閱Matlab幫助文檔

一點點廢話

Matlab有着很是詳細的幫助文檔,建議直接閱讀其幫助文檔,市面上不少Matlab書籍都是簡單的翻譯翻譯幫助文檔,例子都是照抄,還有不少錯誤和斷章取義的地方,參考這樣的書籍容易被帶上彎路。數據結構

打開Matlab,按F1打開幫助,此部份內容在:編輯器

MATLAB->Advanced Software Development->MATALB API for Other Languages函數

什麼是MEX-file

簡單來講MEX-file是一種預編譯的,用其餘語言(C/C++,Fortran)編寫的函數庫,能夠直接被Matlab調用。spa

正如前面提到的,這種方式適用於兩種狀況:scala

  1. 程序中有一部分代碼耗時巨大,想經過改寫這部分函數提升速度
  2. 已經有大量C/C++或Fortran的函數庫,想直接用Matlab調用,避免重複開發

這兩種狀況用MEX-file的這種方案來解決都是很是合適的,由於這種調用方式很是方便,你須要注意地只是數據結構的轉換。這種方式支持C/C++和Fortran,本文主要將C/C++。翻譯

如何建立可供Matlab直接調用的MEX-file

1.安裝Matlab支持的編譯器

在Matlab命令窗口輸入:debug

mex -setup 

若是你的電腦已經安裝了Matlab支持的編譯器,這時候你應該會看到設置編譯器的提示命令,跟着一步步下去就能夠了。指針

注意:若是你電腦只安裝了一個支持的編譯器,這一步會自動用此編譯器進行配置,若是有多個支持的編譯器,Matlab會引導你選擇要使用哪一個編譯器。 若是你電腦沒有安裝合適的編譯器,會獲得一個錯誤,提示你安裝合適的編譯器,並給出一個支持編譯器列表的連接。code

2.建立.c/.cpp文件

這一步能夠用Matlab的編輯器也能夠用其餘你喜歡的編輯器,須要注意的是: 未來在Matlab中調用的函數名即爲此處你建立的文件名,而不是文件內的函數名

MEX-file的內容

一個完整的MEX-file應該包括:

  • #include <mex.h> MEX-file頭文件
  • mexFunction 入口函數(C/C++中的main函數)
  • 輸入輸出的數據的校驗
  • 變量的傳遞
  • 你本身編寫的功能函數

mexFunction 入口函數

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

此函數是MEX-file的入口函數,形式比較固定,起着C/C++語言中main函數的做用,建議放在整個文件的最後。

mexFunction函數中通常只作數據的轉換和其餘函數的調用,不作複雜的處理。

  • prhs -函數右側,輸入參數
  • plhs -函數左側,輸出參數
  • nrhs -函數右側,輸入參數的個數
  • nlhs -函數左側,輸出參數的個數

例如:在Matlab中用[a,b]=myMEX(c,d,e)的形式調用的函數,則nrhs==3 表明有三個輸入參數,nlhs==2表明有兩個輸入參數,參數值分別儲存在prhsplhs中。 輸入輸出數據的校驗

這一部分建議放在mexFunction裏面,校驗輸入輸出參數的個數是否符合要求,校驗輸入參數的類型是否符合要求。

這裏的輸入參數是 只讀 的,不要嘗試更改,否則會引發錯誤。

建立一個可更改的輸入參數的副本myData並調用mxDuplicateArray函數:

mxArray *myData = mxCreateStructMatrix(1,1,nfields,fnames); mxSetField(myData,0,"myFieldName",mxDuplicateArray(prhs[0])); 

對於輸入參數類型的校驗能夠用mxIsClass中的函數來進行:

if(mxIsSparse(prhs[1])||mxIsComplex(prhs[1])||mxIsClass(prhs[1],"char")) { mexErrMsgTxt("input2 must be full matrix of real values."); } 

完整的mxIsClass函數列表見附錄。

3.變量的傳遞

這一部分主要涉及如何將輸入參數中的數據傳出,而且用C/C++的數據結構來表示,以及如何構建輸出參數,將運算結果傳回Matlab。

因爲Matlab中數據結構種類比較多,且比較複雜,這裏並不會一一涉及,只介紹幾種比較經常使用的數據類型,其餘數據類型你們能夠自行查閱Matlab幫助文檔。

如下的示例代碼都假設你須要傳遞的輸入參數是第一個,若是爲其餘,只需修改prhs的角標便可

標量的傳遞

size_t mrows; //行數 size_t ncols; //列數 double scalar; //接收輸入參數的變量 mrows = mxGetM(prhs[0]); //獲取矩陣行數 ncols = mxGetN(prhs[0]); //獲取矩陣列數 /*校驗輸入是不是一個標量*/ if( !mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]) || !(mrows==1 && ncols==1) ) { mexErrMsgIdAndTxt( "MATLAB:timestwo:inputNotRealScalarDouble","Input must be a noncomplex scalar double."); } scalar = mxGetScalar(prhs[0]); //獲取標量值 

矩陣的傳遞

size_t mrows; //行數 size_t ncols; //列數 mxArray *inMat; //接收輸入參數的指針 mrows = mxGetM(prhs[0]); //獲取矩陣行數 ncols = mxGetN(prhs[0]); //獲取矩陣列數 /*校驗輸入是不是一個3*4的double矩陣 矩陣維數的校驗也能夠去掉(相應的你的處理函數要有處理不一樣大小矩陣的能力)*/ if( !mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]) || !(mrows==3 && ncols==4) ) { mexErrMsgIdAndTxt( "MATLAB:timestwo:inputNotRealScalarDouble", "Input must be a noncomplex double matrix."); } /*獲取輸入矩陣的指針*/ inMat = mxGetPr(prhs[0]); 

爲輸出變量分配內存並傳遞給mexFunction的輸出參數

mxArray *outMat; outMat = mxCreateDoubleMatrix((mwSize)mrows,(mwSize)ncols,mxREAL); plhs[0] = outMat; 

這裏須要注意的是Matlab中矩陣的儲存是列優先的,而C語言中是行優先的,在調用矩陣元素時須要注意:

double result; /* 將iMat中的第 i行 j列的元素值賦給result */ result = inMat[j*mrows+i] 

爲輸出變量分配內存並傳遞給mexFunction的輸出參數

mxArray *outMat; outMat = mxCreateDoubleMatrix((mwSize)mrows,(mwSize)ncols,mxREAL); plhs[0] = outMat; 

字符串的傳遞

將輸入參數轉換爲C-typestring

string input_buf; input_buf = mxArrayToString(prhs[0]); 

爲輸出字符串分配內存

string output_buf; output_buf=mxCalloc(buflen, sizeof(char)); 

將輸出字符串傳遞給輸出參數

plhs[0] = mxCreateString(output_buf); 

最後釋放內存

mxFree(input_buf); 

Structure和Cell類型的傳遞

StructureCell類型的傳遞其實與其餘類型類似,他們是mxArray類型。

mxGetFieldmxGetCell函數能夠用來獲取指向StructureCell類型內容的mxArray類型的指針。

mxGetNumberOfFieldsmxGetNumberOfElements能夠用來獲取Structure的條目的個數和元素的個數。

mxGetData函數能夠用來獲取mxArray變量中包含的數據。

由於Matlab中Cell的應用比Structure頻繁,而且這二者結構數據傳遞方式很相似,此處以Cell進行講解: 假設咱們的輸入參數Cell中第一個元素是一個1x3的矩陣,第二個元素仍是一個Cell,這個Cell裏面包含兩個1x3的矩陣,在Matlab中構建方法以下:

temp = []; temp{1} = [1:3]; temp{2} = [4:6]; Cell = []; Cell{1} = [1:3]; Cell{2} = temp; 

如今咱們若是咱們想將Cell傳入MEX-file中進行處理,讀出Cell中第第一個元素[1:3]和第二個元素temp,這個元素仍是一個Cell,這在Matlab中很常見,能夠以下操做:

mxArray *mat; //指向第一個元素[1:3]的指針 mxArray *Cell; //指向第二個元素的指針,仍是一個Cell size_t nrows; //行數 size_t ncols; //列數 double *data; //數據 int i; //循環變量 int j; /* 獲取輸入Cell的維數 */ mrows = mxGetM(prhs[0]); ncols = mxGetN(prhs[0]); /* 輸出Cell的維數,這裏做爲示例我並無保存Cell的維數,後面獲取Cell中元素維數時仍是用的這兩個變量 */ mxPrintf("rows:%d,cols:%d\n",mrows,ncols); /* 取出Cell中第一個元素,此處mat是一個指向矩陣的mxArray指針,data儲存的是數據 */ mat = mxGetCell(prhs[0],0); data = (double*)mxGetData(mat); /* 打印矩陣內的元素 [1:3]*/ mrows = mxGetM(mat); ncols = mxGetN(mat); for (i=0;i<mrows;i++) { for (j=0;j<ncols;j++) { mxPrintf("%f ",data[j*M+i]); } mxPrintf("\n"); } /* 取出Cell中第二個元素 仍是一個Cell 再取出裏面內容的方法與上述過程一致 繼續調用mxGetCell */ Cell = mxGetCell(prhs[0],1); 

關於在Mex-file中構建Cell的方法,這裏不詳細講了,由於我的以爲這麼作吃力不討好,何不把數據分別傳入Matlab再從新組織呢?若是你真的想要在MEX-file裏面構建Cell並傳出,原理是建立一個相應大小的mxArray,由於Cell自己就是mxArrary類型的,而後將這部份內存的地址傳給plhs

MEX-file的編譯和調用

將Matlab的當前目錄切換到你MEX-file所在的目錄,假設你的文件名爲helloMEX.c,在Matlab命令窗口輸入

mex helloMEX.c 

若是獲得MEX completed successfully.的提示即爲編譯成功,若是不成功,會顯示錯誤的位置和緣由,對應修改便可。

編譯成功後會獲得後綴爲.mexw64的文件(後綴名與平臺相關,此爲win64下的後綴名,其餘平臺不一樣),將此文件添加入Matlab的路徑中,或者將當前目錄切換到此文件所在目錄,便可像普通的Matlab函數同樣調用此文件。

附錄: mxIsClass 函數列表

(更詳細的介紹參見Matlab幫助文檔)

mxIsDouble() //Determine whether mxArray represents data as double-precision, floating-point numbers mxIsSingle() //Determine whether array represents data as single-precision, floating-point numbers mxIsComplex() //Determine whether data is complex mxIsNumeric() //Determine whether array is numeric mxIsInt64() //Determine whether array represents data as signed 64-bit integers mxIsUint64() //Determine whether array represents data as unsigned 64-bit integers mxIsInt32() //Determine whether array represents data as signed 32-bit integers mxIsUint32() //Determine whether array represents data as unsigned 32-bit integers mxIsInt16() //Determine whether array represents data as signed 16-bit integers mxIsUint16() //Determine whether array represents data as unsigned 16-bit integers mxIsInt8() //Determine whether array represents data as signed 8-bit integers mxIsUint8() //Determine whether array represents data as unsigned 8-bit integers mxIsChar() //Determine whether input is string array mxIsLogical() //Determine whether array is of type mxLogical mxIsLogicalScalar() //Determine whether scalar array is of type mxLogical mxIsLogicalScalarTrue() //Determine whether scalar array of type mxLogical is true mxIsStruct() //Determine whether input is structure array mxIsCell() //Determine whether input is Cell array mxIsClass() //Determine whether array is member of specified class mxIsInf() //Determine whether input is infinite mxIsFinite() //Determine whether input is finite mxIsNaN() //Determine whether input is NaN (Not-a-Number) mxIsEmpty() //Determine whether array is empty mxIsSparse() //Determine whether input is sparse array mxIsFromGlobalWS() //Determine whether array was copied from MATLAB global workspace mxAssert() //Check assertion value for debugging purposes mxAssertS() //Check assertion value without printing assertion text
相關文章
相關標籤/搜索