MDA之路

          MDA,UML,XML,Eclipse及Java相關(guān)的Blog
          posts - 53, comments - 494, trackbacks - 0, articles - 2
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
           

          m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)

          在工程計(jì)算相關(guān)項(xiàng)目中,常常利用Matlab來(lái)完成計(jì)算、算法、繪圖等功能。使用Matlab來(lái)完成這些功能非常簡(jiǎn)單,Matlab提供的m編程語(yǔ)言功能強(qiáng)大,代碼量少。為了在自己的C/C++項(xiàng)目中加入這些功能,需要一系列繁瑣的過(guò)程,令很多人望之卻步。主要的困難在于:

          l         如何從m文件生成VC可用的C/C++代碼;

          l         如何設(shè)置編譯參數(shù),在VC中編譯這些代碼;

          l         如何在C/C++語(yǔ)言中設(shè)置輸入輸出參數(shù),使之與M代碼生成的C++代碼一同運(yùn)行;

          l         如何制作包含matlab運(yùn)行時(shí)庫(kù)的安裝程序。

          下面結(jié)合網(wǎng)絡(luò)上面的資料,對(duì)以上問(wèn)題進(jìn)行了總結(jié),較好的解決了上面的問(wèn)題。我使用的相關(guān)開(kāi)發(fā)環(huán)境如下:Matlaba6.5VC6WindowsXP

          1       引子

          進(jìn)入正文之前,要說(shuō)說(shuō)寫這篇文章的起因。近幾天發(fā)現(xiàn)一個(gè)半年前寫的程序出現(xiàn)了莫名其妙的bug。在程序退出時(shí)總有一個(gè)線程死掉不能退出,導(dǎo)致整個(gè)進(jìn)程不能正常退出,必須從進(jìn)程管理器中殺掉。由于該程序一共有7個(gè)子線程,我一個(gè)個(gè)檢查后發(fā)現(xiàn)程序在運(yùn)行時(shí)有一個(gè)并非由我創(chuàng)建的多余子線程。通過(guò)Process ViewerSpy++等工具觀察發(fā)現(xiàn),該線程中有以下幾個(gè)窗口IMETthreadWindowMSCTFIMEUI,查來(lái)查去毫無(wú)頭緒。

          首先懷疑是多線程庫(kù)有bug,因此仔細(xì)重讀了一遍自己封裝的多線庫(kù),還真的發(fā)現(xiàn)了幾個(gè)bug,但是修正后于事無(wú)補(bǔ)。花費(fèi)了三天時(shí)間;

          然后懷疑是界面庫(kù)有問(wèn)題,仔細(xì)比較了使用界面庫(kù)和不使用界面庫(kù)前后的差別發(fā)現(xiàn)界面庫(kù)會(huì)多啟動(dòng)兩個(gè)界面管理線程,但是都會(huì)正常退出,沒(méi)有問(wèn)題。花費(fèi)一天時(shí)間;

          最后只好懷疑是引入的dll啟動(dòng)了某個(gè)線程。一個(gè)個(gè)排查dll,終于發(fā)現(xiàn)了ago4501.dllv4501v.dll這兩個(gè)可疑的dll。這兩個(gè)dll是由Mideva(將m文件轉(zhuǎn)換為C/C++代碼的一個(gè)中間工具)引入的。

          當(dāng)初使用Mideva就是因?yàn)樵谥苯邮褂?/span>matlabmcc出現(xiàn)了困難,不得已找mideva代替。很多人還信誓旦旦的說(shuō)Mideva是最適宜VC使用的m代碼轉(zhuǎn)換工具。很多書和網(wǎng)絡(luò)資料都還給出了示例代碼,我就不相信他們沒(méi)有碰到這個(gè)線程問(wèn)題,只是避而不談罷了。想起Mideva已經(jīng)被MathWork公司收購(gòu)并且不再支持了,我就決心放棄mideva,繼續(xù)使用mcc來(lái)生成代碼。所有的歷程都記錄如下。

          2       M文件轉(zhuǎn)換為C/C++文件

          要在VC中使用m文件,方法有很多種。

          最簡(jiǎn)單的我認(rèn)為還是使用Mideva,當(dāng)然如果你能夠搞定那個(gè)線程問(wèn)題,并且永遠(yuǎn)只使用matlab6以前的版本,你就可以使用mideva。這里就不介紹了。

          第二種就是使用Matlab引擎來(lái)調(diào)用m文件,也比較簡(jiǎn)單,但是你必須在目標(biāo)機(jī)器上安裝matlab才行,這往往是不現(xiàn)實(shí)的。

          第三種使用mccm文件編譯成為C/C++代碼,然后導(dǎo)入Vc編譯,因?yàn)槌3I珊芏嘣创a,使用很繁瑣,這個(gè)很多網(wǎng)絡(luò)資料已經(jīng)說(shuō)過(guò)。

          第四種就是使用mccm文件編譯為頭文件、dlllib然后導(dǎo)入VC編譯。目前這是最可行的一種方法。本文引用了首發(fā)于哈工大紫丁香站BBSfork (撒哈拉沙漠的沙)寫的解決方法。并做了一些文字上的修改。Fork的例子有些簡(jiǎn)單,沒(méi)有涉及多維數(shù)據(jù)參數(shù)的構(gòu)建與輸入,也沒(méi)有多字符串組參數(shù)的構(gòu)建,因此我重寫了一個(gè)較為實(shí)用的例子來(lái)展示他的內(nèi)容。

          2.1             例子

          例子的內(nèi)容是通過(guò)輸入的數(shù)據(jù)來(lái)展示農(nóng)作物產(chǎn)量的統(tǒng)計(jì)圖,其m代碼如下:

          function result = MyStat(mStatMatrix,mNameMatrix,n)

          % 畫出柱狀圖來(lái)展示各個(gè)不同季度的農(nóng)作物產(chǎn)量

          % mStatMatrix代表農(nóng)作物產(chǎn)量矩陣,每行為一個(gè)地區(qū),每行第一列為小麥產(chǎn)量,第二列為玉米產(chǎn)量;

          % mNameMatrix代表地區(qū)名稱字符串?dāng)?shù)組;

          % n代表地區(qū)個(gè)數(shù)

          % 返回值為所有地區(qū)糧食總產(chǎn)量

          bar(mStatMatrix);

          xlabel('地區(qū)名稱');

          ylabel('產(chǎn)量');

          title('農(nóng)作物產(chǎn)量統(tǒng)計(jì)');

          legend('小麥','玉米',1);

          totalnum = 0;

          for i=1:n

           text(i,max(mStatMatrix(i,1),mStatMatrix(i,2))+0.25,mNameMatrix(i));

           totalnum = totalnum + mStatMatrix(i,1)+mStatMatrix(i,2);

          end

          set(gcf,'Menubar','none');

          result = totalnum;

          matlab中輸入如下命令:

          data=[1,2;3,4;5,6;1,1]

          name={'1號(hào)地區(qū)','15號(hào)地區(qū)','7號(hào)地區(qū)','9號(hào)地區(qū)'}

          n=4

          MyStat(data,name,n)

          可以得到圖如下:


          返回值為
          23

          2.2             Mcc生成代碼

          輸入:(格式:mcc -t -W libhg:<庫(kù)名稱> -T link:lib -h libmmfile.mlib libmwsglm.mlib 文件名) 

          mcc -t -W libhg:MyStatLib -T link:lib -h libmmfile.mlib libmwsglm.mlib MyStat

          然后你會(huì)在你的工作目錄下找到MyStatLib.dllMyStatLib.libMyStatLib.h三個(gè)文件。這三個(gè)文件就是VC編程所需要的。一個(gè)有趣的bug是,你的庫(kù)名稱不能和m文件名稱相同,否則mcc會(huì)報(bào)錯(cuò),因?yàn)橛行┲虚g文件重名了

          2.3             VC中添加

          VC中建一個(gè)基于對(duì)話框的MFC應(yīng)用程序,名字為TestStat,添加一個(gè)按鈕,并添加按鈕響應(yīng)函數(shù),函數(shù)內(nèi)容在第五步中說(shuō)明。將上面生成的3個(gè)文件拷貝到VC工程的TestStat目錄里。

          2.4             設(shè)置VC

          VC中選擇:工程--->設(shè)置,再選屬性表Link選項(xiàng),下拉菜單中選擇Input,在對(duì)象/庫(kù)模塊中加入附錄A中所列出的內(nèi)容,注意用空格將它們格開(kāi)而在忽略。庫(kù)中加入附錄B中列出的內(nèi)容;再選擇屬性表C/C++選項(xiàng),下拉菜單選General,在預(yù)處理程序定義中添加附錄C中的內(nèi)容,原來(lái)有的內(nèi)容要保留,并注意用逗號(hào)將它們隔開(kāi)。再選擇下拉菜單的Precompiled Headers選項(xiàng),選擇“自動(dòng)使用預(yù)補(bǔ)償頁(yè)眉”,在其中添加stdafx.h,確定。

          2.5             設(shè)置頭文件和庫(kù)文件路徑

          選擇:工具--->選擇,屬性頁(yè)選擇“目錄”,在include files里面加入:

          C:"MATLAB6P5"EXTERN"INCLUDE

          C:"MATLAB6P5"EXTERN"INCLUDE"CPP 

          注意,根據(jù)你的matlab的安裝位置的不同,要相應(yīng)的修改上面的地址。在Library files里面加入:

          C:"MATLAB6P5"EXTERN"LIB"WIN32 

          C:"MATLAB6P5"EXTERN"LIB"WIN32"MICROSOFT"MSVC60 

          注意,根據(jù)你的matlab的安裝位置的不同,要相應(yīng)的修改上面的地址。

          2.6             添加響應(yīng)代碼

          在按鈕響應(yīng)函數(shù)所在文件中添加如下的頭文件:詳細(xì)的解釋見(jiàn)下一章參數(shù)問(wèn)題。

              ...... 

              #include "mystatlib.h"

              ...... 

              函數(shù)響應(yīng)代碼為: 

                 mxArray * mStatMatrix = NULL;

                 mxArray * mNameMatrix = NULL;

                 mxArray * n;

                 //2維數(shù)組賦值,是一個(gè)3*2數(shù)組

                 mStatMatrix = mxCreateDoubleMatrix(4,2,mxREAL);

                 int mrows = mxGetM(mStatMatrix); //行數(shù)

                 int ncols = mxGetN(mStatMatrix); //列數(shù)

                 double* data = mxGetPr(mStatMatrix); //矩陣的數(shù)據(jù)地址

                 double setdata[4][2] = {{1,2},{3,4},{5,6},{7,8}};    //源數(shù)據(jù),也可為二維數(shù)組

                 for (int i = 0; i < mrows; i++)

                 {

                        for (int j = 0; j < ncols; j++)

                        {

                               data[j*mrows+i] = setdata[i][j]; //注意這里的賦值,相當(dāng)于轉(zhuǎn)置矩陣賦值

                        }

                 }

                 //創(chuàng)建一個(gè)Cell數(shù)組來(lái)存放字符串?dāng)?shù)組

                 int dim[1] ;

                 dim[0] = 4;

                 mNameMatrix = mxCreateCellArray(1,dim);

                 //Cell數(shù)組賦值

                 for (int x = 0; x < 4; x++)

                 {

                        char szTmp[10];

                        sprintf(szTmp,"地區(qū)%d",x+1);

                        mxArray* m = mxCreateString(szTmp);

                        mxSetCell(mNameMatrix,x,m);

                 }

                 //n賦值

                 n = mxCreateScalarDouble(4);

                 MyStatLibInitialize();

                 mlfMystat(mStatMatrix,mNameMatrix,n);

                 mxDestroyArray(mNameMatrix);

                 mxDestroyArray(mStatMatrix);

                 mxDestroyArray(n);

          2.7             添加自己的庫(kù)

          Link---->Input選項(xiàng)中加入一項(xiàng):MyStatLib.lib。這就是我們從m文件編譯過(guò)來(lái)的dll的庫(kù)文件。

          2.8             編譯鏈接執(zhí)行

          可得到以下界面:

          2.9             附錄

          附錄A:鏈接庫(kù) 

          libmmfile.lib libmatlb.lib libmx.lib libmat.lib libmatpm.lib sgl.lib libmwsglm.lib libmwservices.lib libut.lib

          附錄B:忽略庫(kù) 

          msvcrt.lib 

          附錄C: 預(yù)處理程序定義 

          MSVC,IBMPC,MSWIND 

          附錄D:進(jìn)一步參考 

          mxArray的使用參考matlab網(wǎng)站的cmath_ug2b.pdf

          3       參數(shù)問(wèn)題

          Matlab中最常使用的變量有三種,分別是標(biāo)量、矩陣和元胞數(shù)組(Cell Array),我們只要掌握了這三種變量就可以對(duì)付大部分的需求了。在上面的例子中m函數(shù)MyStat(mStatMatrix,mNameMatrix,n)有三個(gè)輸入?yún)?shù),分別是二維矩陣mStatMatrix,元胞數(shù)組mNameMatrix和標(biāo)量n

          mStatMatrix代表農(nóng)作物產(chǎn)量矩陣,每行為一個(gè)地區(qū),每行第一列為小麥產(chǎn)量,第二列為玉米產(chǎn)量;

          mNameMatrix代表地區(qū)名稱字符串?dāng)?shù)組;

          n代表地區(qū)個(gè)數(shù)。

          3.1             mxArray標(biāo)量

          建立一個(gè)標(biāo)量最簡(jiǎn)單,只要將標(biāo)量的值作為參數(shù)傳入即可:

          n = mxCreateScalarDouble(3);

          3.2             mxArray矩陣

          建立多維矩陣比較簡(jiǎn)單,但是給矩陣賦值則比較復(fù)雜。建立一個(gè)雙精度數(shù)矩陣的函數(shù)如下:

          mStatMatrix = mxCreateDoubleMatrix(4,2,mxREAL);

          前兩個(gè)參數(shù)代表二維矩陣是一個(gè)4*2的矩陣,最后一個(gè)代表這是一個(gè)實(shí)數(shù)矩陣。

          給二維矩陣賦值是較為復(fù)雜的,首先要通過(guò)mxGetPr函數(shù)來(lái)得到矩陣存儲(chǔ)數(shù)據(jù)的地址。然后通過(guò)[]符號(hào)來(lái)進(jìn)行地址偏移將適當(dāng)?shù)闹蒂x值給適當(dāng)?shù)牡刂贰Ee例如下:

                 int mrows = mxGetM(mStatMatrix); //行數(shù)

                 int ncols = mxGetN(mStatMatrix); //列數(shù)

                 double* data = mxGetPr(mStatMatrix); //矩陣的數(shù)據(jù)地址

                 double setdata[4][2] = {{1,2},{3,4},{5,6},{7,8}};    //源數(shù)據(jù)

                 for (int i = 0; i < mrows; i++)

                 {

                        for (int j = 0; j < ncols; j++)

                        {

                               data[j*mrows+i] = setdata[i][j]; //注意這里的賦值,相當(dāng)于轉(zhuǎn)置矩陣賦值

                        }

                 }

          給多維數(shù)組賦值時(shí)要特別注意:第一,mxArray的存儲(chǔ)是先列后行的,而C語(yǔ)言是先行后列的,所以在賦值時(shí)相當(dāng)于使用轉(zhuǎn)置矩陣來(lái)賦值;第二要仔細(xì)防止下標(biāo)越界,如果越界則程序運(yùn)行時(shí)會(huì)崩潰。

          3.3             元胞數(shù)組

          元胞數(shù)組是matlab獨(dú)有的數(shù)據(jù)類型。相當(dāng)于將各種不同類型的變量集中到一個(gè)數(shù)組里面。此處我們用元胞數(shù)組來(lái)存儲(chǔ)多個(gè)字符串。

          創(chuàng)建元胞數(shù)組的函數(shù)如下:

          mxArray *mxCreateCellArray(int ndim, const int *dims);

          參數(shù)ndim指示元胞數(shù)組的維數(shù),參數(shù)dims實(shí)際上是一個(gè)int數(shù)組,存儲(chǔ)了各維的長(zhǎng)度。下面創(chuàng)建了一個(gè)一維數(shù)組,長(zhǎng)度為4.

                 //創(chuàng)建一個(gè)Cell數(shù)組來(lái)存放字符串?dāng)?shù)組

                 const int dim[1] = {3};

                 mNameMatrix = mxCreateCellArray(1,dim);

          Cell數(shù)組賦值比較簡(jiǎn)單,即使用mxCreateString創(chuàng)建多個(gè)字符串然后用mxSetCell將字符串賦值給元胞數(shù)組:

                 for (int x = 0; x < 4; x++)

                 {

                        char szTmp[10];

                        sprintf(szTmp,"地區(qū)%d",x+1);

                        mxArray* m = mxCreateString(szTmp);

                        mxSetCell(mNameMatrix,x,m);

                 }

          3.4             調(diào)用m代碼中的函數(shù)

          參數(shù)準(zhǔn)備完畢就可以調(diào)用函數(shù)了。Dll中會(huì)提供很多可調(diào)用的函數(shù),有兩個(gè)主要的函數(shù),一個(gè)名稱為XXXInitialize()XXX即為庫(kù)名稱,本文中是MyStatLibInitialize);第二個(gè)是mlfXXX(參數(shù)列表)。調(diào)用函數(shù)分兩步,第一步調(diào)用初始化函數(shù)MyStatLibInitialize;第二步用設(shè)置好的參數(shù)調(diào)用mlfMystat(mStatMatrix,mNameMatrix,n)

          記得調(diào)用完成后用mxDestroyArray刪除mxArray占用的內(nèi)存。至此為止,代碼編寫工作全部結(jié)束。程序可以正常運(yùn)行了,但是別笑,噩夢(mèng)剛剛開(kāi)始~~~~

          4       打包

          要使編寫的程序能夠在其他機(jī)器順利運(yùn)行,必須制作安裝程序。于其他開(kāi)發(fā)庫(kù)不同,matlab程序的打包顯得比較困難。尤其是要脫離matlab環(huán)境運(yùn)行的程序顯得更加困難。

          根據(jù)fork(撒哈拉沙漠的沙)在哈工大紫丁香站BBS上面的文章,我簡(jiǎn)要總結(jié)了一種較為簡(jiǎn)單的打包方法。

          首先得到matlab運(yùn)行時(shí)庫(kù),其方法是運(yùn)行“MATLAB6p5"extern"lib"win32目錄下“mglinstaller.exe”程序,這個(gè)程序會(huì)在指定目錄產(chǎn)生bintoolbox兩個(gè)目錄,大小是23.7M。這就是matlab的運(yùn)行時(shí)庫(kù);

          第二,在制作安裝程序時(shí),將這兩個(gè)運(yùn)行時(shí)庫(kù)加入安裝資源,在安裝時(shí)拷貝到指定目錄C:"MATLAB6p5p1(根據(jù)你自己開(kāi)發(fā)程序上的matlab安裝目錄來(lái)寫),記住必須拷貝到同樣的目錄,因?yàn)?/span>mcc生成的代碼中對(duì)路徑有硬編碼;

          第三,在制作安裝程序時(shí),添加Path路徑的命令,在安裝時(shí)設(shè)置pathC:"MATLAB6p5p1"bin"win32(根據(jù)你自己開(kāi)發(fā)程序上的matlab安裝目錄來(lái)寫);

          第四,安裝完成后必須重啟,否則Path路徑不起作用,這一點(diǎn)我很奇怪,因?yàn)橐话銇?lái)說(shuō)不會(huì)這樣


          評(píng)論

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2008-04-14 08:12 by 飄千雪
          謝謝樓主,幫了我的大忙!~
          希望lz能寫出更好的文章:)

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2008-08-25 18:48 by llt
          謝謝

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)[未登錄](méi)  回復(fù)  更多評(píng)論   

          2008-10-16 12:10 by david
          thanks
          Having a question.

          version:matlab7.0

          command failed?
          mcc -t -W libhg:MyStatLib -T link:lib -h libmmfile.mlib libmwsglm.mlib MyStat

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2008-11-01 15:55 by anye
          實(shí)在太好!!!比書上寫的還詳盡,非常的容易懂!多謝樓主!

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2009-01-08 23:13 by tt
          好文章,謝謝!

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2009-01-08 23:13 by tt
          這樣的好人再多些吧!#%%%

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2011-04-06 10:19 by wjt0426
          @david
          我的也出錯(cuò)了,不知道你解決了這個(gè)問(wèn)題了嗎?請(qǐng)教!

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)[未登錄](méi)  回復(fù)  更多評(píng)論   

          2013-07-08 00:13 by 羅勇
          樓主您好,請(qǐng)問(wèn)一個(gè)問(wèn)題:若函數(shù)的輸出量是一個(gè)元胞數(shù)組,該如何初始化呢?例如,Y=cal(mat1,mat2);函數(shù)實(shí)現(xiàn)的目的是將兩個(gè)矩陣mat1和mat2合并到一個(gè)元胞數(shù)組Y里,編譯之后,在VC中應(yīng)該如何對(duì)Y做初始化?十分感謝!

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2013-12-06 21:55 by tj
          C:\Documents and Settings\HHUC\My Documents\MATLAB\TestStat\TestStatDlg.cpp(237) : error C2660: 'mlfMyStat' : function does not take 3 parameters

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2014-09-18 15:32 by sforeverm
          請(qǐng)問(wèn),如果是多個(gè)m文件,其中一個(gè)m文件會(huì)調(diào)用另外的m文件,如何完成上述工作?特別是添加響應(yīng)函數(shù)時(shí)如何編寫?

          # re: m文件轉(zhuǎn)換為C/C++文件的編譯、繪圖、參數(shù)、打包問(wèn)題總結(jié)  回復(fù)  更多評(píng)論   

          2015-07-06 21:58 by tanya
          請(qǐng)問(wèn)樓主,使用的是哪個(gè)版本的MATLAB?
          我現(xiàn)在是MATLAB2014a,但是出現(xiàn)無(wú)法運(yùn)行compiler的問(wèn)題
          主站蜘蛛池模板: 玉屏| 新邵县| 英吉沙县| 昂仁县| 毕节市| 万安县| 大同县| 政和县| 麦盖提县| 汽车| 厦门市| 武平县| 如东县| 义乌市| 沛县| 南通市| 宁晋县| 旌德县| 永吉县| 航空| 苗栗市| 云浮市| 无棣县| 嘉兴市| 乌拉特中旗| 武功县| 浦城县| 藁城市| 潜江市| 青浦区| 个旧市| 嫩江县| 泾川县| 永新县| 偃师市| 扬中市| 周口市| 固原市| 临西县| 四子王旗| 云梦县|