天天看點

VC++和Matlab混合程式設計一、前言二、Matlab與C語言混合程式設計方法簡介。三、 如何把Matlab下的.m檔案轉化為VC 可調用的動态連結庫 四.直接用C程式設計

以下文章轉載自http://www.cnblogs.com/mfryf/archive/2012/02/16/2354295.html

一、前言

        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.1    VC下的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.32    Matlab編譯前的準備工作。

        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,選擇角度選項,按計算按鈕即可得到結果。

 四.直接用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.liblib /def:libmatlb.def /machine:ix86 /out:libmatlab.liblib /def:libmmfile.def /machine:ix86 /out:libmmfile.liblib /def:libmx.def /machine:ix86 /out:libmx.lib 

        不論是方式(1)還是(2),生成的libmx.lib libmatlb.lib libmmfile.lib libmat.lib檔案都要拷貝到c:\matlab\extern\lib(也就是添加到VC的編譯路徑中去)。

        本文中的檔案路徑可能跟讀者計算機中的路徑有所不同,請參照修改。

繼續閱讀