我們除了可以經由 MATLAB 來呼叫由 C 程式語言所寫的 MATLAB 函數之外,也可以反向操作,也就是經由獨立的 C 程式來呼叫 MATLAB 引擎,此種情況通常發生在你已經有一個大型的 C 程式碼,但是可能想要使用 MATLAB 的特殊功能來完成某些特定的工作,例如:
想要使用 MATLAB 的強大數學功能,例如計算反矩陣、行列式、最小平方解、奇異值分解(Singlar Value Decomposition)等。
想要使用 MATLAB 強大的圖形顯示功能,例如二維及三維圖形、動畫等。
Hint 如果你想將一個獨立的 MATLAB 視窗應用程式轉換成單獨的可執行檔(Standalone Executable,亦即不需要安裝 MATLAB 即可運作的 .exe 檔案),那麼你需要的是適用於MATLAB 5.x 和 6.x 的「Runtime Server」工具箱,就可以將你的 MATLAB 視窗應用程式(不需要命令列輸入的 MATLAB 應用程式)轉成 .exe 檔案,包含所有的圖形顯示功能,可以讓沒有安裝 MATLAB 的其他人使用。 在 MATLAB 7.x,「Runtime Server」工具箱已經不復存在,其功能已經搬到「MATLAB Compiler」工具箱了。
首先,我們來看一個簡單的範例,其功能是在 C 程式碼裡面啟動 MATLAB 並執行一個 M 檔案 plotSine.m,同時秀出執行 whos 指令後的訊息,C 的範例程式碼是 plotViaMatlab01.c。假設你已經設定好你的 C 編譯器(請見本章第二節),而且假設你用的是 Microsoft VC 10.0,那麼我們可在 MATLAB 輸入下列命令以編譯此 C 程式碼:
Example 1: 03-應用程式介面/matlabEngine01.m optsFile = [matlabroot '\bin\win64\mexopts\msvc100engmatopts.bat'];
mex('-f', optsFile, 'plotViaMatlab01.c'); % 進行編譯
!plotViaMatlab01 % 測試程式
Hint 如果你的作業系統是 32-bit Windows,請將上述範例之 "win64" 改為 "win32". 如果你使用的編譯器是 Microsoft VC 8.0,請將上述範例之 "msvc100engmatopts.bat" 改為 "msvc80engmatopts.bat",依此類推。
若一切無誤,將會產生 plotViaMatlab01.exe,當你執行此程式碼後,會啟動另一個 MATLAB,並執行 plotSine.m,畫出下列圖形:
同時跳出一個訊息視窗,以顯示 MATLAB 在執行 whos 指令後的輸出訊息:
同時工作列也會顯示一個 MATLAB 的圖示,代表 MATLAB engine 正在執行中。當你按下訊息視窗的「確定」後,MATLAB engine 也就跟著結束了。
plotViaMatlab01.c 的內容如下:
原始檔(03-應用程式介面/plotViaMatlab01.c ): (灰色區域按兩下即可拷貝) /* 此範例說明如何在微軟的視窗環境下,由 C 程式來呼叫 MATLAB 引擎 */
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <direct.h>
#include "mex.h"
#include "engine.h"
#define BUFSIZE 256
int PASCAL WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow){
char buffer[BUFSIZE];
mxArray *app;
Engine *ep;
/* 啟動 MATLAB 引擎 */
if (!(ep = engOpen(NULL))){ // 產生一個 MATLAB 引擎物件
MessageBox ((HWND)NULL, (LPSTR)"Can't start MATLAB engine",
(LPSTR)"plotViaMatlab01.c", MB_OK);
exit(-1);
}
/* 切換目錄並執行 plotSine.m */
_getcwd(buffer, BUFSIZE); // 將此程式所在目錄存入字串 buffer
app = mxCreateString(buffer); // 產生 MATLAB 內部的字串變數 app
engPutVariable(ep, "appDir", app); // 將字串變數 app 至入工作空間的變數 appDir
engEvalString(ep, "cd(appDir)"); // 將 MATLAB 的工作目錄切換至字串 appDir 所指定的目錄
engEvalString(ep, "plotSine"); // 執行同目錄下的 plotSine.m
/* 取得 MATLAB 輸出訊息 */
engOutputBuffer(ep, buffer, BUFSIZE); // 設定 buffer 可以接收 MATLAB 的輸出訊息
engEvalString(ep, "whos"); // 在 MATLAB 引擎執行 whos 指令
MessageBox((HWND)NULL, (LPSTR)buffer, (LPSTR) "MATLAB - whos", MB_OK); // 顯示 buffer 的內容
engClose(ep); // 最後關閉 MATLAB 引擎
return(0);
}
我們已經將相關的說明,都以註解的方式放在程式碼之中。主要的重點可以說明如下。
首先我們要產生一個 MATLAB Engine的物件,才能經由這個物件啟動 MATLAB 以及和 MATLAB 溝通,這步可以經由下列程式碼完成:
Engine *ep=engOpen(NULL);
再來假設欲呼叫的MATLAB程式 plotSine.m 與呼叫它的 C 程式是放在同一個目錄,我們必須將 MATLAB 切換到此目錄,作法如下:
將此程式所在目錄存入字串 buffer:_getcwd(buffer, BUFSIZE );
產生 MATLAB 內部的字串變數 app:app = mxCreateString(buffer);
將字串變數 app 至入工作空間的變數 appDir:engPutVariable(ep, "appDir", app);
將 MATLAB 的工作目錄切換至字串 appDir 所指定的目錄:engEvalString(ep, "cd(appDir)");
最後就將我們想要呼叫的 MATLAB 程式 plotSine.m 傳入執行,即可大功告成:
engEvalString(ep, "plotSine");
接著我們要顯示 MATLAB 在執行 whos 之後所得到的輸出訊息,作法如下:
設定 buffer 可以接收 MATLAB 的輸出訊息:engOutputBuffer(ep, buffer, BUFSIZE);
在 MATLAB engine 執行 whos 指令:engEvalString(ep, "whos");
顯示 buffer 的內容:MessageBox ((HWND)NULL, (LPSTR)buffer, (LPSTR) "MATLAB - whos", MB_OK);
最後關閉 MATLAB engine:engClose(ep);
在上述 MATLAB 範例程式中,如果發生編譯錯誤,可能是你所使用的編譯器不是 Microsoft VC 10.0,此時要將編譯參數檔 optsFile 改成適合你所用的編譯器,常用的編譯參數檔案可以顯示如下:
Example 2: 03-應用程式介面/showOpsFile.m % 如果你的作業系統是 32-bit Windows,請將下列之 "win64" 改為 "win32".
dir([matlabroot '\bin\win64\mexopts\*engmatopts.bat'])
intelc11msvs2008engmatopts.bat msvc100freeengmatopts.bat
intelf11msvs2008engmatopts.bat msvc80engmatopts.bat
intelf11msvs2008shellengmatopts.bat msvc90engmatopts.bat
msvc100engmatopts.bat
由上述範例可以看出,編譯參數檔案名稱都符合 *engmatopts.bat 的格式,根據你使用的編譯器,就可以找到相關的編譯參數檔。
Hint
在下面這個範例中,我們在 C 程式碼內反覆將 MATLAB 命令送到 MATLAB 執行,而不用呼叫一個外部的 M 檔案。編譯及執行的範例如下:
Example 3: 03-應用程式介面/matlabEngine02.m optsFile = [matlabroot '\bin\win64\mexopts\msvc100engmatopts.bat'];
mex('-f', optsFile, 'plotViaMatlab02.c'); % 進行編譯
!plotViaMatlab02 % 測試程式
執行 plotViaMatlab02.exe 之後,可以畫出「自由落體的距離與時間關係圖」,如下:
plotViaMatlab02.c 程式碼如下:
原始檔(03-應用程式介面/plotViaMatlab02.c ): (灰色區域按兩下即可拷貝) /* 此範例說明如何在微軟的視窗環境下,由 C 程式來呼叫 MATLAB 引擎 */
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "engine.h"
int PASCAL WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow){
Engine *ep;
mxArray *T;
double time[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
/* 啟動 MATLAB 引擎 */
if (!(ep = engOpen(NULL))) {
MessageBox ((HWND)NULL, (LPSTR)"Can't start MATLAB engine",
(LPSTR)"plotViaMatlab02.c", MB_OK);
exit(-1);
}
T = mxCreateDoubleMatrix(1, 10, mxREAL); // 產生一個 MATLAB 的內部變數 T
memcpy((char *)mxGetPr(T), (char *)time, 10*sizeof(double)); // 將 time 的值拷貝到 T
engPutVariable(ep, "T", T); // 將 T 的值送到 MATLAB 工作空間的變數 T
/* 自由落體的位移對時間的函數:distance = (1/2)g.*t.^2 */
engEvalString(ep, "D = .5.*(-9.8).*T.^2;"); // 將字串送到 MATLAB 去執行
engEvalString(ep, "plot(T, D, 'o-');"); // 畫出執行結果
engEvalString(ep, "title('自由落體的距離與時間關係圖');");
engEvalString(ep, "xlabel('時間 (秒)');");
engEvalString(ep, "ylabel('距離 (米)');");
mxDestroyArray(T); // 移除 MATLAB 內部變數 T
engClose(ep); // 最後關閉 MATLAB 引擎
return(0);
}
在上述範例中,相關的說明都以註解的方式寫在上述程式碼內,在此不再贅述。
有關更進一步如何使用 C 呼叫 MATLAB 引擎,可查閱 MATLAB Helpdesk 中的 External Interfaces/API 章節「Calling MATLAB from C and Fortran Programs」,有更詳細的介紹。
MATLAB程式設計:進階篇