摘要:Matlab具備很強的數值計算和分析等能力,而C/C++是目前最爲流行的高級程序設計語言,二者互補結合的混合編程在科學研究和工程實踐中具備很是重要的意義。從Matlab調用C/C++代碼及C/C++調用m文件兩方面,深刻地研究了它們之間混合編程的原理和實現機制,而且給出了具體條件下的混合編程方法和步驟。實驗代表,給出的Matlab與C/C++混合編程接口及應用方法是有效、實用的。html
1引言c++
Matlab是當前應用最爲普遍的數學軟件,具備強大的數值計算、數據分析處理、系統分析、圖形顯示甚至符號運算等功能[1]。利用這一完整的數學平臺,用戶能夠快速實現十分複雜的功能,極大地提升工程分析計算的效率[2][3]。但與其餘高級程序[3]相比,Matlab程序是一種解釋執行程序,不用編譯等預處理,程序運行速度較慢[4]。編程
C/C++語言是目前最爲流行的高級程序設計語言之一[5]。它可對操做系統和應用程序以及硬件進行直接操做,用C/C++語言明顯優於其它解釋型高級語言,一些大型應用軟件如 Matlab 就是用C語言開發的。windows
在工程實踐中,用戶常常遇到Matlab與C/C++混合編程的問題。本文基於Matlab 6.5和VC6.0開發環境,在Windows平臺下就它們之間的混合編程問題進行深刻研究並舉例說明。服務器
2 Matlab調用C/C++函數
Matlab調用C/C++的方式主要有兩種:利用MEX技術和調用C/C++動態鏈接庫。工具
在Matlab與C/C++混合編程以前,必須先對Matlab的編譯應用程序mex和編譯器mbuild進行正確的設置[1]:性能
對Matlab編譯應用程序mex的設置:Mex –setup.ui
對Matlab編譯器mbuild的設置:Mbuild –setup.操作系統
2.1調用C/C++的MEX文件
MEX是Matlab Executable的縮寫,它是一種「可在Matlab中調用的C(或Fortran)語言衍生程序」[6]。MEX文件的使用極爲方便,其調用方式與Matlab的內建函數徹底相同,只需在Matlab命令提示符下鍵入MEX文件名便可。
一個C/C++的MEX源程序一般包括4個組成部分,其中前3個是必須包含的內容,第4個則根據所實現的功能靈活選用:(1)#include 「mex.h」;(2)MEX文件的入口函數mexFunction, MEX文件導出名必須爲mexFunction函數;(3)mxArray;(4)API函數
經過簡單的例子說明C/C++的MEX源程序編寫和調用過程:
#include "mex.h"
void timeSTwo(double y[], double x[])
{ y[0] = 2.0*x[0]; }
void mexFunction(int nlhs, mxArray * plhs[], int nrhs, const mxArray *prhs[])
{
double *x,*y; int mrows, ncols;
if( nrhs!=1) mexErrMsgTxt("One input required.");
else if( nlhs>1) mexErrMsgTxt("Too manyoutput arguments");
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." );
plhs[0] = mxCreateDoubleMatrix ( mrows, ncols, mxREAL );
x = mxGetPr( prhs[0] ); y = mxGetPr( plhs[0] ); timestwo(y,x); }
可在matlab中編譯,也能夠直接在C++環境中編譯:
1).(在matlab中)用指令mex timestwo.c編譯此文件,而後在MATLAB命令行下調用生成的MEX文件便可。2). (在VC2008中)和通常c++同樣編譯後,就會產生dll,這樣能夠直接在Matlab中用了,或者copy且更改後綴名.mexw32便可。(由於Matlab R2010b之後版本可能不支持調用dll爲後綴的mex文件了)
2.2調用C/C++動態鏈接庫(即:通常普通的C程序dll沒有用mex的接口函數)
Matlab提供對動態鏈接庫DLL文件的接口[7]。利用該接口,可在Matlab中調用動態鏈接庫導出的函數。Matlab對DLL的接口支持各類語言編寫的DLL文件。在調用DLL文件以前,須要準備函數定義的頭文件。對於C/C++語言開發的DLL文件,可以使用源程序中相應的頭文件;而對於其餘語言開發的DLL,則要手工準備等效的C語言函數定義頭文件。
在Matlab中利用動態鏈接庫接口技術一般須要完成如下4個步驟:
(1)打開動態鏈接庫文件;(2)爲調用函數準備數據;(3)調用動態鏈接庫文件中導出的函數;(4)關閉動態鏈接庫文件。
爲了實現以上步驟,用到的Matlab函數有:loadlibrary, loadlibrary, calllib, libfunctions, lipointer, libstruct, libisloaded。下面舉例說明Matlab調用C/C++動態鏈接庫的方法和步驟:
a.在VC環境下,新建工程->win32動態鏈接庫->工程名Test1->empty工程->完成;
b.新建->C++源文件->添加a.cpp,內容爲:#include "a.h"
_declspec(dllexport) int add(int a, int b) { return a+b; }
c.新建->C/C++頭文件->添加a.h,內容爲: _declspec(dllexport) int add(int a,intb); 而後編譯生成Test1.dll動態鏈接庫文件,將Test1.dll和a.h拷到Matlab 工做目錄下。
d.在Matlab命令行下,調用Test.dll:>>loadlibrary(‘Test1’,’a.h’); >>x=7;
>>y=8; >>calllib(‘Test1’,‘add’,x,y); Ans=15 >>unloadlibrary(‘Test1’).
調用DLL動態鏈接庫的方法,爲Matlab重用工程實踐中積累的大量實用C/C++代碼提供了一種簡潔方便的方法。與調用MEX文件相比,該方法更加簡便實用。可是這個接口之支持C,不支持C++庫和函數的重載,這種狀況下,推薦用MEX-file,若實在要用這種方法(調用C/C++動態鏈接庫),則對於C++要作一些更改,詳見http://www.mathworks.de/help/techdoc/matlab_external/f43202.html#bq__4nu-1,
3 C/C++調用Matlab
在工程實踐中,C/C++調用Matlab的方法主要有調用Matlab計算引擎、包含m文件轉換的C/C++文件,以及調用m文件生成的DLL文件。
3.1利用Matlab計算引擎
Matlab的引擎庫爲用戶提供了一些接口函數,利用這些接口函數,用戶在本身的程序中以計算引擎方式調用Matlab文件。該方法採用客戶機/服務器的方式,利用Matlab引擎將Matlab和C/C++聯繫起來。在實際應用中,C/C++程序爲客戶機,Matlab做爲本地服務器。
C/C++程序向Matlab計算引擎傳遞命令和數據信息,並從Matlab計算引擎接收數據信息[2]。
Matlab提供瞭如下幾個C語言計算引擎訪問函數供用戶使用[8]:engOpen,engClose, engGetVariable,engPutVariable,engEvalString,engOutputBuffer,engOpenSingleUse, engGetVisible,engSetVisible。
下面以C語言編寫的、調用Matlab引擎計算方程x3 ?2x+5=0根的源程序example2.c爲例,說明C/C++調用Matlab計算引擎編程的原理和步驟:
#include <windows.h> #include <stdlib.h>
#include <stdio.h> #include "engine.h"
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow )
{
Engine *ep; mxArray *P=NULL,*r=NULL;
char buffer[301]; double poly[4] = { 1,0,-2,5 };
if ( !(ep =engOpen(NULL) ) )
{ fprintf( stderr,"\nCan't start MATLAB engine\n" ); return EXIT_FAILURE; }
P = mxCreateDoubleMatrix( 1, 4, mxREAL); mxSetClassName( P, "p" );
memcpy( ( char * ) mxGetPr( P ), (char *) poly, 4*sizeof(double) );
engPutVariable( ep, P ); engOutputBuffer( ep, buffer, 300 );
engEvalString( ep, "disp(['多項式',poly2str(p,'x'),'的根']),r=roots(p)" );
MESSageBox(NULL,buffer,"example2展現MATLAB引擎的應用",MB_OK);
engClose( ep ); mxDestroyArray( P ); return EXIT_SUCCESS;
}
在Matlab下運行example2.exe: mex -f example2.c。運行結果如圖1所示:
利用計算引擎調用Matlab的特色是:節省大量的系統資源,應用程序總體性能較好,但不能脫離Matlab的環境運行,且運行速度較慢,但在一些特別的應用[9](例如須要進行三維圖形顯示)時可考慮使用。
3.2利用mcc編譯器生成的cpp 和hpp文件
Matlab自帶的C++Complier--mcc,能將m文件轉換爲C/C++代碼。所以,它爲C/C++程序調用m文件提供了另外一種便捷的方法。下面舉例說明相應步驟:
a.新建example3.m: function y=exmaple3(n) y=0; for i=1:n y=y+i; end
保存後在命令窗口中輸入:mcc -t -L Cpp -h example3.
則在工做目錄下生成example3.cpp和example3.hpp兩個文件。
b.在VC中新建一個基於對話框的MFC應用程序Test2,添加一個按鈕,並添加按鈕響應函數,函數內容見f步。將上面生成的兩個文件拷貝到VC工程的Test2目錄下。
c.在VC中選擇:工程->設置,選擇屬性表Link選項,下拉菜單中選擇Input,在對象 / 庫模塊中加入lIBMmfile.lib libmatlb.lib libmx.lib libmat.lib libmatpm.lib sgl.lib libmwsglm.lib libmwservices.lib,(後三個爲使用Matlab圖形庫時,需加入)注意用空格分開;而在忽略庫中加入 msvcrt.lib;
d.選擇屬性表C/C++選項,下拉菜單選General,在預處理程序定義中保留原來有的內容,並添加MSVC,IBMPC,MSWIND,並用逗號隔開。選擇下拉菜單的Precompiled Headers 選項,在「自動使用預補償頁眉」中添加stdafx.h,而後肯定。
e.選擇:工具-> 選項,屬性頁選擇「目錄」,在include files加入: C:\MATLAB6p5p1\extern\include, C:\MATLAB6p5p1\extern\include\cpp;而後在 Library files裏面加入: C:\MATLAB6p5p1\bin\win32, C:\MATLAB6p5p1\extern\ lib\win32\microsoft\msvc60;注意根據用戶的Matlab安裝位置,修改相應目錄。
f.在響應函數中添加頭文件:#include "matlab.hpp" #include "example3.hpp"函數響應代碼爲:
int i; mwArray n; n=10; n=example3(n); i=n.ExtractScalar(1);
CString str; str.Format( "example3的返回值是:%d", I ); AfxMessageBox( str );
g.編譯,鏈接,執行,結果如圖2所示。
3.3利用mcc編譯器生成的DLL 文件
Matlab的C++ Complier不只可以將Matlab的m文件轉換爲C/C++的源代碼,還能產生徹底脫離Matlab運行環境的獨立可執行DLL程序。從而能夠在C/C++程序中,經過調用DLL實現對 Matlab代碼的調用。下面經過一個簡單的例子說明C/C++調用m文件生成的DLL:
a.創建m文件example4.m: function result = example4(para)
x=[1 para 3]; y=[1 3 1]; plot( x,y ); result=para*2; end.而後在命令窗口中輸入:
mcc -t -W libhg: example4 -T link: lib -h libmmfile.mlib libmwsglm.mlib example4則在工做目錄下會生成example4 .dll、example4 .lib和example4 .h三個文件。
b.在VC中新建一個基於對話框的應用程序Test3,而後添加一個按鈕及按鈕響應函數,函數內容見d步,再將生成的3個文件拷貝到Test2工程目錄下。
c.VC編譯環境的設置如同3.2節c、d步;
d.在按鈕函數文件添加以下的頭文件:#include "example4 .h",函數響應代碼爲:
mxArray*para=mxCreateDoubleScalar(2); mxArray* result; example4Initialize();
result =mlfExample4(para); CString str;
str.Format( "%f",mxGetScalar(result) ); AfxMessageBox(str);
e.編譯,鏈接,執行,結果如圖3所示。
利用mcc編譯器生成的DLL動態鏈接庫文件,只需在C/C++編譯環境中將其包含進來,調用導出函數便可實現原m文件的功能,極大地方便了用戶的代碼設計。
4例子
轉自:http://sxnuwhui.blog.163.com/blog/static/13706837320124282524436/
讓 Matlab調用C函數是經過DLL文件實現的,而這個DLL的開發過程不只僅可使用Matlab自帶的mex命令,還可使用VC++開發環境,使用 VC有不少好處,一是讓我回到了原來熟悉的開發環境中,二可以使用更加標準的C++編譯器,第三點也是最爽的,可使用VC強大的調試功能。
這裏我分四個步驟講解一個簡單的開發過程,使用的Matlab版本爲Matlab R2008a,VC版本爲Visual C++ 2008 Express版本。
步驟1、新建一個空的dll程序
步驟2、爲matlab函數提供頭文件和靜態庫的支持
要讓衆多的Matlab接口函數可以運行起來,就必須給它們提供頭文件和靜態庫的支持,爲了不每一個項目都作這些添加工做,咱們能夠在Tools|Options|VC++ Directories下將頭文件和靜態庫的目錄添加進去,如圖:
在Include files中添加」$matlab dir\extern\include」和」$matlab dir\extern\include\win32」,在Library files中添加」$matlab dir\extern\lib\win32\microsoft」。
在項目屬性中,讓連接器連接libmx.lib、libmat.lib和libmex.lib,如圖:
在Preprocessor Definitions中添加MATLAB_MEX_FILE的宏定義。在Project|xx Project Properties|C/C++|Preprocessor中設置,以下圖:
如今寫一個hello world程序就能夠編譯經過,如:
第三步 利用def文件導出mexFunction函數
讓函數從DLL中的導出有兩種方法,__declspec(dllexport)命令導出和def文件導出,但因爲須要導出mexFunction在」mex.h」中已經定義,因此前一種導出方法行不通,這裏採用def文件導出。
首先告訴編譯器我使用了xx.def文件,在Project|xx Project Properties|linker|input|Module Definition File中填上xx.def。
再編寫xx.def的內容,只需兩句話:
LIBRARY xx
EXPORTS mexFunction
這裏的xx指的是模塊名稱(我用的是我新建的項目名稱),並將def文件添加到你的工程中。
最後,將輸出文件的後綴名改成mexw32。在Project|xx Project Properties|linker|General|Output File中,如圖:
再次編譯、生成,能夠在生出目錄mexw32文件。能夠用dumpbin工具查看mexw32文件,能夠看到有函數mexFunction已經導出。到這個時候DLL開發已經完成。下一步:
第四步、在啓動調試時關聯matlab
在Project|xx Project Properties|Debugging|Command裏添加Matlab的啓動地址:」$matlab dir\bin\win32\MATLAB.exe」。如圖:
最後設定斷點,啓動調試,Matlab會自動啓動,運行編寫的DLL(mexw32)文件,就會觸發斷點,如:
固然,最終使用的mexw32文件還應該是Release版本的,這樣的話,上面談的部分設置還須要在Release版本中從新來過。
5結束語
本文從Matlab調用C/C++代碼和C/C+調用m文件兩方面,詳細地研究了Matlab與C/C++混合編程技術。對於Matlab調用C/C++代碼,給出了經常使用的MEX技術和調用C/C++動態鏈接庫的方法,並對它們進行比較。針對用戶在實際中常常遇到的C/C++調用Matlab問題,經過研究給出了經常使用的三種方法及其特色:利用Matlab計算引擎的方法,混合編程後的可執行程序脫離不了Matlab的運行環境,運行速度很慢;利用mcc編譯器將m文件轉化爲C/C++文件的方法,雖然能獨立於Matlab運行環境,可在C/C++環境中包含生成的文件很是繁瑣;可是m文件生成的DLL爲用戶提供了一種簡潔方便的C/C++調用Matlab代碼的方法。除 Matlab自帶的mcc外,Matcom 也能將M文件編譯爲C/C++文件和DLL文件[2][8],但混合編程原理同樣,在此省略。
matlab調用c++代碼的詳細步驟已經在個人另一篇博客中(matlab和c++混合編程---方法和步驟)說明,這裏再也不重複。按照它說明的步驟,mex編譯一個簡單的.cpp文件沒有任何問題。可是若是你編譯的.cpp文件裏面include和lib了一些外部的頭文件和靜態庫,例如用到了opencv庫或是某個深度相機的SDK等,仍是簡單的mex這個cpp文件就會報錯,提示找不到某個頭文件。這是由於matlab和vs的環境配置沒有完成。我就是被這個問題困擾了兩天,如今貼出來,之後謹記。
首先,在vs中配置環境。
將你用到的SDK的頭文件,靜態庫和動態庫配置好,詳細過程參見 vs項目中頭文件(.h)靜態庫(.lib)和 動態庫(.dll )的路徑和配置問題;
將matlab安裝目錄下的extern文件夾中的包含文件夾,靜態庫文件夾配置到vs項目中,例如個人是‘D:\programs installing\matlab2016\extern\include’和‘D:\programs installing\matlab2016\extern\lib\win64\microsoft’,這一步能夠參考我轉載的博文matlab和c++混合編程---方法和步驟。
而後,在matlab中配置環境。
matlab的環境配置和vs不一樣,MATLAB裏面都是用命令配置編譯環境的,第一步,mex -setup,根據提示一步一步來就能夠了;第二步,編譯,若是是簡單的cpp文件,直接mex xxxxxx.cpp就能夠了,若是這個.cpp文件裏面包含附加依賴項和外部靜態庫,僅僅簡單的mex這個文件就會報錯,這時須要在mex這個文件的同時,將用到的外部包含庫和靜態庫添加進去(詳細查閱一下mex這個函數的用法),例如我須要將包含目錄D:\myo-sdk-win-0.9.0\include,lib目錄D:\myo-sdk-win-0.9.0\lib以及lib文件myo32和myo64配置進去,就須要這樣命令:mex SEMGStream.cpp -I'D:\myo-sdk-win-0.9.0\include' -L'D:\myo-sdk-win-0.9.0\lib' -lmyo32 -lmyo64。
須要說明的是,matlab的環境配置跟平時的添加路徑不同,及時你在matlab的設置路徑裏面將用到的外部庫目錄添加進去,用mex編譯的時候仍是會報錯,只能經過mex函數的參數進行路徑包含,我就是被這個誤區困擾了兩天,但願看到的朋友避免一樣的問題。