3-8 qW萿 C {茤Is MATLAB

我們除了可以經由 MATLAB 來呼叫由 C 程式語言所寫的 MATLAB 函數之外,也可以反向操作,也就是經由獨立的 C 程式來呼叫 MATLAB 引擎,此種情況通常發生在你已經有一個大型的 C 程式碼,但是可能想要使用 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.moptsFile = [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);
}

我們已經將相關的說明,都以註解的方式放在程式碼之中。主要的重點可以說明如下。

  1. 首先我們要產生一個 MATLAB Engine的物件,才能經由這個物件啟動 MATLAB 以及和 MATLAB 溝通,這步可以經由下列程式碼完成:
    Engine *ep=engOpen(NULL);
  2. 再來假設欲呼叫的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)");
  3. 最後就將我們想要呼叫的 MATLAB 程式 plotSine.m 傳入執行,即可大功告成:
  4. engEvalString(ep, "plotSine");
  5. 接著我們要顯示 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);
  6. 最後關閉 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
若在編譯或執行過程中,還有其他錯誤訊息,可以到美國總公司的技術支援網頁,填入相關關鍵字以進行搜尋,網址是:http://www.mathworks.com/support

在下面這個範例中,我們在 C 程式碼內反覆將 MATLAB 命令送到 MATLAB 執行,而不用呼叫一個外部的 M 檔案。編譯及執行的範例如下:

Example 3: 03-應用程式介面/matlabEngine02.moptsFile = [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程式設計:進階篇