Matlab與C/C++混合編程 Matlab調(diào)用C函數(shù)

作者博客:bot7.cc

Matlab與C/C++混合編程有很多種方式,分別適用于不同的情況。

  1. 程序主體用Matlab編寫,有一些特別耗時的函數(shù)用C/C++改寫來提高效率,或者已經(jīng)有現(xiàn)成的C/C++函數(shù),應(yīng)用到Matlab程序中(本文屬于這種情況)
  2. 程序主體用C/C++編寫,部分程序想調(diào)用Matlab函數(shù)減少開發(fā)時間,本文不涉及這種情況,建議讀者自行查閱Matlab幫助文檔

一點點廢話

Matlab有著非常詳細(xì)的幫助文檔,建議直接閱讀其幫助文檔,市面上很多Matlab書籍都是簡單的翻譯翻譯幫助文檔,例子都是照抄,還有很多錯誤和斷章取義的地方,參考這樣的書籍容易被帶上彎路。

打開Matlab,按F1打開幫助,此部分內(nèi)容在:

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

什么是MEX-file

簡單來說MEX-file是一種預(yù)編譯的,用其他語言(C/C++,Fortran)編寫的函數(shù)庫,可以直接被Matlab調(diào)用。

正如前面提到的,這種方式適用于兩種情況:

  1. 程序中有一部分代碼耗時巨大,想通過改寫這部分函數(shù)提高速度
  2. 已經(jīng)有大量C/C++或Fortran的函數(shù)庫,想直接用Matlab調(diào)用,避免重復(fù)開發(fā)

這兩種情況用MEX-file的這種方案來解決都是非常合適的,因為這種調(diào)用方式非常方便,你需要注意地只是數(shù)據(jù)結(jié)構(gòu)的轉(zhuǎn)換。這種方式支持C/C++和Fortran,本文主要將C/C++。

如何創(chuàng)建可供Matlab直接調(diào)用的MEX-file

1.安裝Matlab支持的編譯器

在Matlab命令窗口輸入:

mex -setup

如果你的電腦已經(jīng)安裝了Matlab支持的編譯器,這時候你應(yīng)該會看到設(shè)置編譯器的提示命令,跟著一步步下去就可以了。

注意:如果你電腦只安裝了一個支持的編譯器,這一步會自動用此編譯器進(jìn)行配置,如果有多個支持的編譯器,Matlab會引導(dǎo)你選擇要使用哪個編譯器。
如果你電腦沒有安裝合適的編譯器,會得到一個錯誤,提示你安裝合適的編譯器,并給出一個支持編譯器列表的鏈接。

2.創(chuàng)建.c/.cpp文件

這一步可以用Matlab的編輯器也可以用其他你喜歡的編輯器,需要注意的是:
將來在Matlab中調(diào)用的函數(shù)名即為此處你創(chuàng)建的文件名,而不是文件內(nèi)的函數(shù)名

MEX-file的內(nèi)容

一個完整的MEX-file應(yīng)該包括:

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

mexFunction 入口函數(shù)

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

此函數(shù)是MEX-file的入口函數(shù),形式比較固定,起著C/C++語言中main函數(shù)的作用,建議放在整個文件的最后。

mexFunction函數(shù)中一般只做數(shù)據(jù)的轉(zhuǎn)換和其他函數(shù)的調(diào)用,不做復(fù)雜的處理。

  • prhs -函數(shù)右側(cè),輸入?yún)?shù)
  • plhs -函數(shù)左側(cè),輸出參數(shù)
  • nrhs -函數(shù)右側(cè),輸入?yún)?shù)的個數(shù)
  • nlhs -函數(shù)左側(cè),輸出參數(shù)的個數(shù)

例如:在Matlab中用[a,b]=myMEX(c,d,e)的形式調(diào)用的函數(shù),則nrhs==3 代表有三個輸入?yún)?shù),nlhs==2代表有兩個輸入?yún)?shù),參數(shù)值分別儲存在prhsplhs中。
輸入輸出數(shù)據(jù)的校驗

這一部分建議放在mexFunction里面,校驗輸入輸出參數(shù)的個數(shù)是否符合要求,校驗輸入?yún)?shù)的類型是否符合要求。

這里的輸入?yún)?shù)是 只讀 的,不要嘗試更改,不然會引起錯誤。

創(chuàng)建一個可更改的輸入?yún)?shù)的副本myData并調(diào)用mxDuplicateArray函數(shù):

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

對于輸入?yún)?shù)類型的校驗可以用mxIsClass中的函數(shù)來進(jìn)行:

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

完整的mxIsClass函數(shù)列表見附錄。

3.變量的傳遞

這一部分主要涉及如何將輸入?yún)?shù)中的數(shù)據(jù)傳出,并且用C/C++的數(shù)據(jù)結(jié)構(gòu)來表示,以及如何構(gòu)建輸出參數(shù),將運算結(jié)果傳回Matlab。

由于Matlab中數(shù)據(jù)結(jié)構(gòu)種類比較多,且比較復(fù)雜,這里并不會一一涉及,只介紹幾種比較常用的數(shù)據(jù)類型,其他數(shù)據(jù)類型大家可以自行查閱Matlab幫助文檔。

以下的示例代碼都假設(shè)你需要傳遞的輸入?yún)?shù)是第一個,如果為其他,只需修改prhs的角標(biāo)即可

標(biāo)量的傳遞

size_t mrows;  //行數(shù)
size_t ncols;  //列數(shù)
double scalar; //接收輸入?yún)?shù)的變量

mrows = mxGetM(prhs[0]); //獲取矩陣行數(shù)
ncols = mxGetN(prhs[0]); //獲取矩陣列數(shù)

/*校驗輸入是否是一個標(biāo)量*/
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]); //獲取標(biāo)量值

矩陣的傳遞

size_t mrows;    //行數(shù)
size_t ncols;    //列數(shù)
mxArray *inMat;  //接收輸入?yún)?shù)的指針

mrows = mxGetM(prhs[0]); //獲取矩陣行數(shù)
ncols = mxGetN(prhs[0]); //獲取矩陣列數(shù)

/*校驗輸入是否是一個3*4的double矩陣
矩陣維數(shù)的校驗也可以去掉(相應(yīng)的你的處理函數(shù)要有處理不同大小矩陣的能力)*/
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]);

為輸出變量分配內(nèi)存并傳遞給mexFunction的輸出參數(shù)

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

這里需要注意的是Matlab中矩陣的儲存是列優(yōu)先的,而C語言中是行優(yōu)先的,在調(diào)用矩陣元素時需要注意:

double result;

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

為輸出變量分配內(nèi)存并傳遞給mexFunction的輸出參數(shù)

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

字符串的傳遞

將輸入?yún)?shù)轉(zhuǎn)換為C-typestring

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

為輸出字符串分配內(nèi)存

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

將輸出字符串傳遞給輸出參數(shù)

plhs[0] = mxCreateString(output_buf);

最后釋放內(nèi)存

mxFree(input_buf);

Structure和Cell類型的傳遞

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

mxGetFieldmxGetCell函數(shù)可以用來獲取指向StructureCell類型內(nèi)容的mxArray類型的指針。

mxGetNumberOfFieldsmxGetNumberOfElements可以用來獲取Structure的條目的個數(shù)和元素的個數(shù)。

mxGetData函數(shù)可以用來獲取mxArray變量中包含的數(shù)據(jù)。

因為Matlab中Cell的應(yīng)用比Structure頻繁,并且這兩者結(jié)構(gòu)數(shù)據(jù)傳遞方式很類似,此處以Cell進(jìn)行講解:
假設(shè)我們的輸入?yún)?shù)Cell中第一個元素是一個1x3的矩陣,第二個元素還是一個Cell,這個Cell里面包含兩個1x3的矩陣,在Matlab中構(gòu)建方法如下:

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

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

現(xiàn)在我們?nèi)绻覀兿雽?code>Cell傳入MEX-file中進(jìn)行處理,讀出Cell中第第一個元素[1:3]和第二個元素temp,這個元素還是一個Cell,這在Matlab中很常見,可以如下操作:

mxArray *mat;   //指向第一個元素[1:3]的指針
mxArray *Cell;  //指向第二個元素的指針,還是一個Cell
size_t nrows;   //行數(shù)
size_t ncols;   //列數(shù)
double *data;     //數(shù)據(jù)

int i; //循環(huán)變量
int j;

/* 獲取輸入Cell的維數(shù) */
mrows = mxGetM(prhs[0]);
ncols = mxGetN(prhs[0]);
/* 輸出Cell的維數(shù),這里作為示例我并沒有保存Cell的維數(shù),后面獲取Cell中元素維數(shù)時還是用的這兩個變量 */
mxPrintf("rows:%d,cols:%d\n",mrows,ncols);

/* 取出Cell中第一個元素,此處mat是一個指向矩陣的mxArray指針,data儲存的是數(shù)據(jù) */
mat = mxGetCell(prhs[0],0);
data = (double*)mxGetData(mat);

/* 打印矩陣內(nèi)的元素 [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 再取出里面內(nèi)容的方法與上述過程一致 繼續(xù)調(diào)用mxGetCell */
Cell = mxGetCell(prhs[0],1);

關(guān)于在Mex-file中構(gòu)建Cell的方法,這里不詳細(xì)講了,因為個人覺得這么做吃力不討好,何不把數(shù)據(jù)分別傳入Matlab再重新組織呢?如果你真的想要在MEX-file里面構(gòu)建Cell并傳出,原理是創(chuàng)建一個相應(yīng)大小的mxArray,因為Cell本身就是mxArrary類型的,然后將這部分內(nèi)存的地址傳給plhs

MEX-file的編譯和調(diào)用

將Matlab的當(dāng)前目錄切換到你MEX-file所在的目錄,假設(shè)你的文件名為helloMEX.c,在Matlab命令窗口輸入

mex helloMEX.c

如果得到MEX completed successfully.的提示即為編譯成功,如果不成功,會顯示錯誤的位置和原因,對應(yīng)修改即可。

編譯成功后會得到后綴為.mexw64的文件(后綴名與平臺相關(guān),此為win64下的后綴名,其他平臺不同),將此文件添加入Matlab的路徑中,或者將當(dāng)前目錄切換到此文件所在目錄,即可像普通的Matlab函數(shù)一樣調(diào)用此文件。

附錄: mxIsClass 函數(shù)列表

(更詳細(xì)的介紹參見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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容