MDA之路

          MDA,UML,XML,Eclipse及Java相關的Blog
          posts - 53, comments - 494, trackbacks - 0, articles - 2
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理
           

          m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結

          在工程計算相關項目中,常常利用Matlab來完成計算、算法、繪圖等功能。使用Matlab來完成這些功能非常簡單,Matlab提供的m編程語言功能強大,代碼量少。為了在自己的C/C++項目中加入這些功能,需要一系列繁瑣的過程,令很多人望之卻步。主要的困難在于:

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

          l         如何設置編譯參數,在VC中編譯這些代碼;

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

          l         如何制作包含matlab運行時庫的安裝程序。

          下面結合網絡上面的資料,對以上問題進行了總結,較好的解決了上面的問題。我使用的相關開發環境如下:Matlaba6.5VC6;WindowsXP。

          1       引子

          進入正文之前,要說說寫這篇文章的起因。近幾天發現一個半年前寫的程序出現了莫名其妙的bug。在程序退出時總有一個線程死掉不能退出,導致整個進程不能正常退出,必須從進程管理器中殺掉。由于該程序一共有7個子線程,我一個個檢查后發現程序在運行時有一個并非由我創建的多余子線程。通過Process ViewerSpy++等工具觀察發現,該線程中有以下幾個窗口IMETthreadWindowMSCTFIMEUI,查來查去毫無頭緒。

          首先懷疑是多線程庫有bug,因此仔細重讀了一遍自己封裝的多線庫,還真的發現了幾個bug,但是修正后于事無補。花費了三天時間;

          然后懷疑是界面庫有問題,仔細比較了使用界面庫和不使用界面庫前后的差別發現界面庫會多啟動兩個界面管理線程,但是都會正常退出,沒有問題。花費一天時間;

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

          當初使用Mideva就是因為在直接使用matlabmcc出現了困難,不得已找mideva代替。很多人還信誓旦旦的說Mideva是最適宜VC使用的m代碼轉換工具。很多書和網絡資料都還給出了示例代碼,我就不相信他們沒有碰到這個線程問題,只是避而不談罷了。想起Mideva已經被MathWork公司收購并且不再支持了,我就決心放棄mideva,繼續使用mcc來生成代碼。所有的歷程都記錄如下。

          2       M文件轉換為C/C++文件

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

          最簡單的我認為還是使用Mideva,當然如果你能夠搞定那個線程問題,并且永遠只使用matlab6以前的版本,你就可以使用mideva。這里就不介紹了。

          第二種就是使用Matlab引擎來調用m文件,也比較簡單,但是你必須在目標機器上安裝matlab才行,這往往是不現實的。

          第三種使用mccm文件編譯成為C/C++代碼,然后導入Vc編譯,因為常常生成很多源代碼,使用很繁瑣,這個很多網絡資料已經說過。

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

          2.1             例子

          例子的內容是通過輸入的數據來展示農作物產量的統計圖,其m代碼如下:

          function result = MyStat(mStatMatrix,mNameMatrix,n)

          % 畫出柱狀圖來展示各個不同季度的農作物產量

          % mStatMatrix代表農作物產量矩陣,每行為一個地區,每行第一列為小麥產量,第二列為玉米產量;

          % mNameMatrix代表地區名稱字符串數組;

          % n代表地區個數

          % 返回值為所有地區糧食總產量

          bar(mStatMatrix);

          xlabel('地區名稱');

          ylabel('產量');

          title('農作物產量統計');

          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號地區','15號地區','7號地區','9號地區'}

          n=4

          MyStat(data,name,n)

          可以得到圖如下:


          返回值為
          23。

          2.2             Mcc生成代碼

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

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

          然后你會在你的工作目錄下找到MyStatLib.dll,MyStatLib.lib,MyStatLib.h三個文件。這三個文件就是VC編程所需要的。一個有趣的bug是,你的庫名稱不能和m文件名稱相同,否則mcc會報錯,因為有些中間文件重名了。

          2.3             VC中添加

          VC中建一個基于對話框的MFC應用程序,名字為TestStat,添加一個按鈕,并添加按鈕響應函數,函數內容在第五步中說明。將上面生成的3個文件拷貝到VC工程的TestStat目錄里。

          2.4             設置VC

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

          2.5             設置頭文件和庫文件路徑

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

          C:"MATLAB6P5"EXTERN"INCLUDE;

          C:"MATLAB6P5"EXTERN"INCLUDE"CPP 

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

          C:"MATLAB6P5"EXTERN"LIB"WIN32 

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

          注意,根據你的matlab的安裝位置的不同,要相應的修改上面的地址。

          2.6             添加響應代碼

          在按鈕響應函數所在文件中添加如下的頭文件:詳細的解釋見下一章參數問題。

              ...... 

              #include "mystatlib.h"

              ...... 

              函數響應代碼為: 

                 mxArray * mStatMatrix = NULL;

                 mxArray * mNameMatrix = NULL;

                 mxArray * n;

                 //2維數組賦值,是一個3*2數組

                 mStatMatrix = mxCreateDoubleMatrix(4,2,mxREAL);

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

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

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

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

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

                 {

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

                        {

                               data[j*mrows+i] = setdata[i][j]; //注意這里的賦值,相當于轉置矩陣賦值

                        }

                 }

                 //創建一個Cell數組來存放字符串數組

                 int dim[1] ;

                 dim[0] = 4;

                 mNameMatrix = mxCreateCellArray(1,dim);

                 //Cell數組賦值

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

                 {

                        char szTmp[10];

                        sprintf(szTmp,"地區%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             添加自己的庫

          Link---->Input選項中加入一項:MyStatLib.lib。這就是我們從m文件編譯過來的dll的庫文件。

          2.8             編譯鏈接執行

          可得到以下界面:

          2.9             附錄

          附錄A:鏈接庫 

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

          附錄B:忽略庫 

          msvcrt.lib 

          附錄C: 預處理程序定義 

          MSVC,IBMPC,MSWIND 

          附錄D:進一步參考 

          mxArray的使用參考matlab網站的cmath_ug2b.pdf

          3       參數問題

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

          mStatMatrix代表農作物產量矩陣,每行為一個地區,每行第一列為小麥產量,第二列為玉米產量;

          mNameMatrix代表地區名稱字符串數組;

          n代表地區個數。

          3.1             mxArray標量

          建立一個標量最簡單,只要將標量的值作為參數傳入即可:

          n = mxCreateScalarDouble(3);

          3.2             mxArray矩陣

          建立多維矩陣比較簡單,但是給矩陣賦值則比較復雜。建立一個雙精度數矩陣的函數如下:

          mStatMatrix = mxCreateDoubleMatrix(4,2,mxREAL);

          前兩個參數代表二維矩陣是一個4*2的矩陣,最后一個代表這是一個實數矩陣。

          給二維矩陣賦值是較為復雜的,首先要通過mxGetPr函數來得到矩陣存儲數據的地址。然后通過[]符號來進行地址偏移將適當的值賦值給適當的地址。舉例如下:

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

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

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

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

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

                 {

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

                        {

                               data[j*mrows+i] = setdata[i][j]; //注意這里的賦值,相當于轉置矩陣賦值

                        }

                 }

          給多維數組賦值時要特別注意:第一,mxArray的存儲是先列后行的,而C語言是先行后列的,所以在賦值時相當于使用轉置矩陣來賦值;第二要仔細防止下標越界,如果越界則程序運行時會崩潰。

          3.3             元胞數組

          元胞數組是matlab獨有的數據類型。相當于將各種不同類型的變量集中到一個數組里面。此處我們用元胞數組來存儲多個字符串。

          創建元胞數組的函數如下:

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

          參數ndim指示元胞數組的維數,參數dims實際上是一個int數組,存儲了各維的長度。下面創建了一個一維數組,長度為4.

                 //創建一個Cell數組來存放字符串數組

                 const int dim[1] = {3};

                 mNameMatrix = mxCreateCellArray(1,dim);

          Cell數組賦值比較簡單,即使用mxCreateString創建多個字符串然后用mxSetCell將字符串賦值給元胞數組:

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

                 {

                        char szTmp[10];

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

                        mxArray* m = mxCreateString(szTmp);

                        mxSetCell(mNameMatrix,x,m);

                 }

          3.4             調用m代碼中的函數

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

          記得調用完成后用mxDestroyArray刪除mxArray占用的內存。至此為止,代碼編寫工作全部結束。程序可以正常運行了,但是別笑,噩夢剛剛開始~~~~

          4       打包

          要使編寫的程序能夠在其他機器順利運行,必須制作安裝程序。于其他開發庫不同,matlab程序的打包顯得比較困難。尤其是要脫離matlab環境運行的程序顯得更加困難。

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

          首先得到matlab運行時庫,其方法是運行“MATLAB6p5"extern"lib"win32目錄下“mglinstaller.exe”程序,這個程序會在指定目錄產生bintoolbox兩個目錄,大小是23.7M。這就是matlab的運行時庫;

          第二,在制作安裝程序時,將這兩個運行時庫加入安裝資源,在安裝時拷貝到指定目錄C:"MATLAB6p5p1(根據你自己開發程序上的matlab安裝目錄來寫),記住必須拷貝到同樣的目錄,因為mcc生成的代碼中對路徑有硬編碼;

          第三,在制作安裝程序時,添加Path路徑的命令,在安裝時設置pathC:"MATLAB6p5p1"bin"win32(根據你自己開發程序上的matlab安裝目錄來寫);

          第四,安裝完成后必須重啟,否則Path路徑不起作用,這一點我很奇怪,因為一般來說不會這樣


          評論

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

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

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

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

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結[未登錄]  回復  更多評論   

          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文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

          2008-11-01 15:55 by anye
          實在太好?。。”葧蠈懙倪€詳盡,非常的容易懂!多謝樓主!

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

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

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

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

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

          2011-04-06 10:19 by wjt0426
          @david
          我的也出錯了,不知道你解決了這個問題了嗎?請教!

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結[未登錄]  回復  更多評論   

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

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

          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文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

          2014-09-18 15:32 by sforeverm
          請問,如果是多個m文件,其中一個m文件會調用另外的m文件,如何完成上述工作?特別是添加響應函數時如何編寫?

          # re: m文件轉換為C/C++文件的編譯、繪圖、參數、打包問題總結  回復  更多評論   

          2015-07-06 21:58 by tanya
          請問樓主,使用的是哪個版本的MATLAB?
          我現在是MATLAB2014a,但是出現無法運行compiler的問題
          主站蜘蛛池模板: 丰镇市| 镇安县| 安乡县| 玉环县| 东海县| 左权县| 丰台区| 岗巴县| 尚志市| 梓潼县| 连山| 黎川县| 福海县| 南靖县| 吉安县| 蓝山县| 周口市| 清涧县| 云安县| 福州市| 湛江市| 太仓市| 吉水县| 唐山市| 林周县| 肥西县| 杭锦后旗| 凌海市| 元谋县| 新巴尔虎右旗| 横峰县| 错那县| 凤阳县| 陇南市| 临海市| 邻水| 阿图什市| 桑植县| 宜都市| 延川县| 库尔勒市|