一、前言
Matlab是由Mathworks公 司推出的一種應用軟件,最早用於線性代數的教學,由於其豐富的矩陣運算,強大的擴展能力和可靠性,已經被廣泛用於信號處理,系統辨識,仿真,多變量控制, 最優控制,模糊控制,數學工具,神經網絡,它的工具箱內容涉及信號處理,自動控制,圖像處理,經濟,數學,化學等不同領域。同時,MathWorks公司從創立至今始終追蹤各領域的最新進展,這無疑是最明智,最富遠見的舉措。對廣大用戶來說,無疑提供了成功的機會。對於各種理論方案研究來說,Matlab無疑有它的先天優勢,其強大的數據處理能力和豐富的工具箱,使得它的編程極為簡單,可以極大地縮短應用程序開發周期,提高編程效率和縮短理論方案研制周期。
總之,對於純理論方案來說,Matlab語言是優勢多多。但由於Matlab是為了方便用戶而編制的,采用的是類似Basic一樣解釋型機制的語言。所以Matlab語言執行效率很低,只有C語言的十分之一,對於對實時性或速度要求較高的場合來說,Matlab就不太適應了。Matlab是一種高級語言,對底層硬件的控制能力很差,所以對於半實物仿真和偏工程化的產品來說,Matlab並不是一個很好的語言。對於發布軟件公司來說,也希望發布的是一個可執行應用軟件,而不是一個只是Matlab原代碼的產品。
所以把Matlab語言轉換成可執行文件,具有較強的工程實用價值。Microsoft的Visual C++面世以來就受到了風靡全球,其強大硬件控制能力和系統集成功能使無數程序員對它一見傾心。Matlab在它的5.1 版本后,就逐漸增加了對C的支持,本文就對此問題作一簡單探討,歡迎大家批評指正。
二、Matlab與C語言混合編程方法簡介。
Matlab與C語言混合編程有四種方法
A.采用Matlab與C的接口規范來編程。
Matlab與C語言的接口采用稱為MEX的動態鏈接庫方式進行。按MEX接口規范編寫的C原程序經過編譯可生成Matlab動態鏈接子程序,它十分類似於Matlab的內建函數,可有Matlab直接調用。采用此規范可實現對Matlab原代碼的加密。
B.用Matlab引擎來編程
Matlab引擎采用的是客戶機/服務器(Client/Server)的計算方式。所謂客戶機/服務器計算,就是把應用處理負載分布到客戶機和服務器上的工作模式。客戶機與服務器即可以存在於同一台計算機,也可以通過網絡共享信息。一般情況下,客戶機是運行軟件的前端PC機,並且知道如何與服務器通訊;服務器於此對應,是接受信息,並采用相應行動的機器。由於客戶機於服務器共同承擔處理負載,可使系統性能得到極大提高。在一個實際應用中,可用VC活其它C,C++語言作為前端客戶機,它向Matlab引擎發送命令和數據信息,可從Matlab引擎獲得計算結果。
C. 用Matlab下的.m文件轉化為VC可調用動態鏈接庫(DLL)。
下面詳述
D.直接用C編程,通過對Matlab的數學庫函數的調用實現Matlab語言的一般功能。
下面詳述
由於采用方法A和方法B都脫離不了Matlab 運行環境,這里不作詳細介紹,下文主要介紹方法C和方法D。
三、 如何把Matlab下的.m文件轉化為VC 可調用的動態鏈接庫
3.1VC下的DLL簡介
在軟件的開發過程中,DLL已成為常見的編程單元。DLL雖然是可執行文件,但它不能獨立運行,只能被其它應用程序調用。在VC6.0中,MFC支持三種新式DLL
a.通常形式的靜態DLL
b.通常形式的動態MFC的DLL
c.擴展DLL(動態鏈接MFC)
本文采用通常形式的靜態DLL開發和使用DLL應注意以下幾點。
a.DLL頭文件(.H)DLL頭文件是指DLL中說明輸出的類和符號(Symbols),如函數原型和數據結構的.H文件。一方面,它是類和符號原型說明文件,另一方面,當在其它應用程序中調用DLL時,要將該文件包含應用程序中的源文件中。
b.DLL的引入庫文件(.LIB)引入庫文件是DLL在編譯,鏈接成功后的生成的文件。它的主要作用是:當其它應用程序編譯調用DLL時,要將該文件引入應用程序。
c.DLL文件(.DLL)DLL是應用程序調用DLL運行時,真正可執行的代碼文件。
在開發一個DLL 應用程序時,要用到這三個文件。
3.2 Matlab下的Mcc簡介
MCC同樣是Mathworks公司出品的一種應用軟件。可在/matlab/bin
下找到它,它的作用是把.m文件轉換成C/C++文件器。Mcc(版本2.0)用法格式是Mcc [ -選項] fun [fun2 …],如果指定的fun是.m文件,則每個文件都會被轉換成C/C++文件,如指定fun的是.c文件,這些c和相關的c文件將被傳遞給mex和mbuild 程序編譯。Mcc的用法很復雜,具體使用可參見MCC2.0 Online Help.
3.3用.m文件創建一個VC可調用的DLL文件示例
3.31編輯一個子調用的.m文件
基於說明問題起見,用Matlab的edit編了一個簡單的myfunc.m文件,
程序如下圖所示:
fuction y=myfunct(x,b)定義一子調用,程序中
其中x為輸入變量,b為控制選項,b=1時以角度值輸入,b=其它時,
以弧度值輸入,程序通過插值方法求得x的正弦值返回給y。
3.32Matlab編譯前的准備工作。
a. 對Matlab編譯環境進行設置
在Matlab環境中運行mex -setup,按屏幕提示要求選擇編譯器類型,位置等有關信息。如下圖所示:
b.在Matlab環境中運行mbuild -setup,設置方法與上面基本相同,
這兒就不詳述了,mcc和mbuild的設置結果分別保存在mexopts.bat和compopts.bat文件中。
3.33 用mcc將myfunc.m轉換為matlab可調用的DLL
在Matlab環境中運行mcc -t -h -L C -W lib:ppp -T link:lib myfunc.m
下面分別介紹各選項的意義
-t 把M代碼轉換成目標語言。
-h 把輔助編譯器選項打開,打開此選項意味着可以鏈入除Matlab已有的函數外,還可以鏈入作者自編的子函數。
-W lib:ppp 表示生成DLL所需的ppp.h ppp.lib ppp.dll
-T link:lib 表示編譯生成的目標(Target)文件類型為DLL
編譯完成后,將生成如下一些文件
ppp.exp ppp.lib myfunc.c ppp.c ppp.exports myfunc.h ppp.dll ppp.h, 其中有用的文件有三個,分別是ppp.h,ppp.lib,ppp.dll,
它們將會被添加到VC程序中去。
3.34 用MFC編譯一個VC++6.0可執行文件
1.由於dll不能獨立運行,所以要用VC6.0創建一個EXE可執行程序。
在VC6.0中創建一個基於對話框的MFC工程,名為mytest,具體過程參見一般的VC教程。在本例中mytest工程路徑為e:\mat2c\mytest
2.對VC編程環境進行設置。選擇VC編譯器主菜單下Tools->options->
directories,選擇Show Directories for列表框,分別把Matlab的包含文件路徑(如c:\matlab\ertern\include),庫文件(如C:\matlab\extern\lib)路徑添加到VC路徑中去。
3.把ppp.h,ppp.lib,ppp.dll三個復制到e:\mat2c\mytest目錄下,以便Vc調用DLL時能找到這三個文件。
對對話框IDD_MYTEST_DIALOG資源進行編輯,如下圖所示
“ 請輸入數值“,”輸出正弦值“為兩個靜態文本框,兩個Edit為編輯框,分別用來接受輸入數值和輸出數值。對應的變量為雙精度類型的m_din, 和m_dout.。角度“,”弧度“選項表示輸入數值單位制,對應的變量為m_select。“計算“按鈕則啟動計算功能,定義它的ID為ID_ON_ON_CALCULATE,相應的消息處理函數為OnOnCalculate();
|
要使VC能調用DLL,還必須對VC進行以下操作。 (1)引入頭文件和庫文件 在mytestdlg.cpp的頭部加入一行:#include “ppp.h”在project>settings->link下的object/libray modules下加入ppp.lib libmx.lib libmat.lib libmatlb.lib libmmfile.lib目的是讓VC能調用ppp.dll,引入libmx.lib libmat.lib libmatlb.lib 和libmmfile.lib的 目的是讓VC能調用matlab的數學庫函數和一些功能性函數編譯VC與matlab的代碼接口,和作者直接用C寫的一些代碼。 (2)對ppp.h做一點改動。 在#include “matlab.h”語句之后,加入-行extern “C”{,在最后一行#endif之前加入一行}。改動后的ppp.h文件內容如下: #ifndef MLF_V2 #define MLF_V2 1 #endif
#ifndef __ppp_h #define __ppp_h 1
#include "matlab.h" extern "C" { extern mxArray * mlfMyfunc(mxArray * x, mxArray * b); extern void mlxMyfunc(int nlhs, mxArray * plhs[], int nrhs, mxArray * prhs[]); extern void pppInitialize(void); extern void pppTerminate(void); } #endif。
(3)對VC與Matlab接口進行編程 對“計算”按鈕消息處理函數編程如下 void CMytestDlg::OnOnCalculate() { // TODO: Add your control notification handler code here UpdateData(TRUE); pppInitialize(); static double a[1] = { 0.0 }; static double b[1]= { 0.0 }; a[0]=m_din; b[0]=m_select+1; mxArray * A = mclGetUninitializedArray(); mxArray * B = mclGetUninitializedArray(); mxArray * C = mclGetUninitializedArray(); mlfAssign(&A, mlfDoubleMatrix(1, 1, a, NULL)); mlfAssign(&B, mlfDoubleMatrix(1, 1, b, NULL)); mlfAssign(&C, mlfMyfunc(A, B)); double * md=mxGetPr(C); m_dout=md[0]; mxDestroyArray(A); mxDestroyArray(B); mxDestroyArray(C); pppTerminate(); UpdateData(FALSE); } 為了使讀者有一個更深入的了解。對以上關鍵性代碼加以說明。UpdateData(TRUE)表示從屏幕接收數據,a[0]=m_din;表示a[0]存放m_din,即輸入的待計算數值, b[0]=m_select+1;表示 b[0]“角度“,”弧度“的選擇值。 由於Matlab的計算基本單位是矩陣,而VC支持的基本數據類型是int,double等,所以要編寫Matlab與vc之間的接口代碼。如本例中C=myfunc(A,B)的函數,它編譯成動態鏈接庫后C形式代碼為mlfAssign(&C, mlfMyfunc(A, B))。 要使Vc能調用它,必須首先創建三個mxArray *型指針變量 mxArray * A, mxArray * B, mxArray *C指向A,B,C矩陣(mxArray * A = mclGetUninitializedArray(); mxArray *B= ……),由於A,B是輸入變量,故使用 mlfAssign(&A, mlfDoubleMatrix(1, 1, a, NULL)),mlfAssign(&B, mlfDoubleMatrix(1, 1, b, NULL))語句使得A,B矩陣中的元素與 double a[1],static double b[1]內容保持相同。 再使用mlfAssign(&C, mlfMyfunc(A, B))語句調用ppp.dll中的mlfMyfunc函數計算並返回結果到C中. double * md=mxGetPr(C)語句作用是取得返回doulbe *指針,這樣m_dout=md[0]使得m_dout取得的內容就是C矩陣中的第一個元素(即在Matlab語言中為C(1) 的元素,在C/C++語言中,0指示的是數組的第一個元素). 這樣一個DLL 調用就完成了,最終通過UpdateData(FALSE)語句把運算結果顯示出來了。 以上程序中的某些函數用法中參見Matlab中的C Math幫助文件。 (4)作完以上工作后,DLL就已被成功鏈入VC,再經VC編譯器編譯鏈接即可生成可執行文件,運行程序,在對話框中輸入60,選擇角度選項, 按計算按鈕即可得到結果。
4.直接用C編程 直接用C編程也是可以的,它是通過對Matlab的數學庫函數的調用來 實現的,如果能用Matlab實現的語句,就用不着非得用C直接編程因為直接用C編程與把.m文件通過mcc轉換成的C代碼是一樣的的如要實現Matlab中的以下三行功能: A=[1 2 3 4]; B=[4 3 2 1]; C=A+B; 自己直接用C要這樣寫 static double a[4] = { 1.0, 2.0, 3.0, 4.0 }; static double b[4] = { 4.0, 3.0, 2.0, 1.0 }; mxArray * A = mclGetUninitializedArray(); mxArray * B = mclGetUninitializedArray(); mxArray * C = mclGetUninitializedArray(); mlfAssign(&A, mlfDoubleMatrix(1, 4, a, NULL)); mlfAssign(&B, mlfDoubleMatrix(1, 4, b, NULL)); mlfAssign(&C, mlfPlus(A, B)); 而如果用mcc把上面三行轉化為C代碼以后為: static double __Array0_r[4] = { 1.0, 2.0, 3.0, 4.0 }; static double __Array1_r[4] = { 4.0, 3.0, 2.0, 1.0 }; mxArray * A = mclGetUninitializedArray(); mxArray * B = mclGetUninitializedArray(); mxArray * C = mclGetUninitializedArray(); mlfAssign(&A, mlfDoubleMatrix(1, 4, __Array0_r, NULL)); mlfAssign(&B, mlfDoubleMatrix(1, 4, __Array1_r, NULL)); mlfAssign(&C, mlfPlus(A, B)); 它們實質上是一樣的,直接用C編程不如先寫.m代碼,再用mcc工具轉換。 對Matlab與VC編譯器環境的配置工作與上面第3節介紹的一樣。 注意:libmx.lib libmatlb.lib libmmfile.lib libmat.lib 文件並不是Matlab自帶的,Matlab只提供了libmx.dll libmatlb.dll libmmfile.dll libmat.dll 用戶需要自己編譯,在VC有兩種方式實現(推薦方式(2)) (1)VC集成編譯環境中打開 matlab\extern\examples\cppmath\msvc 下的工程文件msvc42.mak,選project->settings->C/C++->code generation 為Debug Multithread Dll選項,Build即可。 (2)把VC的bin目錄下的vcvars32.bat拷貝的C盤根目錄下運行msconfig將vcars32.bat添加的Auoexec.bat中去。 重新啟動計算機。 回到MS_DOS方式下在matlab\extern\include運行 lib /def:libmat.def /machine:ix86 /out:libeng.lib lib /def:libmatlb.def /machine:ix86 /out:libmatlab.lib lib /def:libmmfile.def /machine:ix86 /out:libmmfile.lib lib /def:libmx.def /machine:ix86 /out:libmx.lib
不論是方式(1)還是(2),生成的libmx.lib libmatlb.lib libmmfile.lib libmat.lib文件都要拷貝到c:\matlab\extern\lib(也就是添加到VC的編譯路徑中去)。 本文中的文件路徑可能跟讀者計算機中的路徑有所不同,請參照修改。 |
