VC與Matlab混合編程 及複雜數據:結構體傳遞-轉

本文主要介紹VC與Matlab混合編程的兩種方法,並詳細介紹了VC向Matlab傳遞複雜數據:結構體的方法,有詳細代碼說明。html

Matlab 主要面對科學計算、可視化以及交互式程序設計的高科技計算環境,但因爲Matlab開發平臺上開發的程序不能脫離Matlab運行環境,於是在處理一些實際應用問題時顯得靈活性不足,而VC++則在必定程度上可以彌補這一漏洞,所以,將兩者結合共用,各獻其長,能夠爲科研工做和工程開發提供更爲強大的技術支持。c++

Matlab做爲控制系統設計的一種通用工具,它能夠很方便的和VC進行鏈接。通常而言,Matlab與VC混合編程的實現方法有不少種,這裏主要介紹如下兩種:編程

 

1            VC調用Matlab Engine的方式:數組

Matlab Engine是一組Matlab提供的接口函數,支持C/C++語言,Matlab Engine採用C/S(客戶機/服務器)模式,Matlab做爲後臺服務器,而C/C++程序做爲前臺客戶機,向Matlab Engine傳遞執行命令和數據信息,從Matlab Engine接收執行結果。用戶能夠在前臺應用程序中調用這些接口函數,實現對Matlab Engine的控制。採用這種方法幾乎能利用Matlab所有功能,可是須要在機器上安裝Matlab軟件,缺點是執行效率較低。下面簡單介紹下這種方式的實現步驟:服務器

 

1.1         API接口介紹數據結構

先來介紹一組Matlab提供的引擎API接口:(僅做簡單功能介紹,詳細參數說明請參考Matlab幫助)函數

Engine* engOpen(const char* startcmd)啓動Matlab引擎工具

int engClose(Engine* ep) 關閉Matlab引擎ui

int engEvalString(Engine* ep, const char* string)執行Matlab表達式url

mxArray* engGetArray(Engine* ep, const char* name)獲取一個變量數組的值

int engPutArray(engine* ep, const mxArray* mp)設置一個變量數組的值

int engPutVariable(Engine *ep, const char *name, const mxArray *pm)同上

mxArray *engGetVariable(Engine *ep, const char *name)獲取一個變量

int engOutputBuffer(Engine* eP,char* p,int n)獲取輸出字符串

 

1.2         VC環境配置

要想在VC集成環境下調用Matlab引擎實現VC和Matlab的混合編程,通常須要通過如下幾個必要的步驟:(如下以Matlab2008a和VS2005版本爲例)

(1)添加include路徑:將「\extern\include」(在Matlab的安裝路徑下)路勁添加到VC編譯器的include下

(2)添加lib路徑:將「\extern \lib\win32\microsoft」 (在Matlab的安裝路徑下) 路徑添加到VC編譯器的lib下

(3)加載lib:須要加載至少libmx.lib、libmat.lib、libeng.lib三個庫

(4)include頭文件:在要使用 engine 函數的地方包含engine.h頭文件

 

1.3         引擎調用

接下來就能夠在VC中調用Matlab引擎了,簡單的示例代碼以下:


#include "engine.h"

#pragma comment(lib, "libeng.lib")
#pragma comment(lib, "libmx.lib")
#pragma comment(lib, "libmat.lib")

void TestDeno()
{
    Engine* pEng = NULL;
    if (!(pEng = engOpen(NULL)))
    {
        printf("Open matlab enging fail!");
        return;
    }

    //call Engine plot A*sin(t)+B A=2 B=1

    mxArray *= NULL;

    double init = 2;
    A = mxCreateDoubleMatrix(11, mxREAL);
    memcpy((void*) mxGetPr(A), (void*)&init, sizeof (double));
    engPutVariable(pEng, "A", A);

    init = 1;
    memcpy((void*) mxGetPr(A), (void*)&init, sizeof (double));
    engPutVariable(pEng, "B", A);

    mxDestroyArray(A);

    Sleep(3*60*1000);

    engEvalString(pEng, "t=0:0.2:7;plot(t,A*sin(t)+B);");

    if(NULL != pEng)
    {
        engClose(pEng);
    }
}

示例代碼經過VC調用Matlab引擎,繪製正弦曲線,相對簡單,就再也不詳細解釋,效圖以下:(注意這裏A、B的賦值方式,後續還會說起)

 

VC與Matlab混合編程及複雜數據:結構體傳遞

 

 

2            VC調用Matlab DLL的方式:

DLL是一個可執行的二進制文件。把不少通用的功能放在DLL中,能夠供各類應用程序調用,這樣能夠很好的減小外部存儲空間的佔有量,並實現代碼的共享。Matlab也支持將m程序編譯成dll,供其餘語言(包括VC、VB、Fortran等)調用,下面先簡單介紹下Matlab DLL的編譯步驟:

 

2.1         Matlab DLL製做

(1)配置Matlab的編譯器跟,要將mex和mbuil兩個都配置成本機上安裝的VC,關於mex編譯器的配置參見上一篇博文《S-Function實現simulink》,mbuild的配置方法同mex

(2)編譯m函數成爲dll,在matlab命令空間中使用mcc命令編譯m文件:(mcc的使用參考Matlab幫助)

示例:將一個Matlab函數MyFun 編譯成libMyFun 的命令:

mcc –W cpplib:libMyFun –T link:lib MyFun

參數說明:-W控制編譯以後的封裝格式,cpplib表示c++的lib,冒號以後是編譯輸出lib名,-T表示目標,link:lib表示鏈接到lib的目標,MyFun是待編譯的m文件名,編譯成功以後,會輸出三個文件:libMyFun.lib libMyFun.dll libMyFun.h

 

2.2            DLL在VC中調用

要想在VC集成環境下調用Matlab的DLL,實現VC和Matlab的混合編程,通常須要通過如下幾個必要的步驟:(如下以Matlab2008a和VS2005版本爲例)

(1)VC環境配置,同前面VC調用Matlab引擎方式相似,也須要配置VC的編譯環境,其中include路徑、lib路徑和前一種方式相同,加載的lib變動爲:mclmcrrt.lib、libmx.lib、libmat.lib、mclmcr.lib四個,include的頭文件變動爲:mclmcr.h、matrix.h、mclcppclass.h三個。

(2)DLL中函數的調用,先看一下m函數編譯成C++Dll以後的函數聲明,例:matlab函數:function [y, out] = CaculateFun(x, in)編譯以後對應c++的聲明:void MW_CALL_CONVCaculateFun(int nargout, mwArray& y, mwArray& out, const mwArray& x, const mwArray& in)參數int nargout指定調用時,輸出參數的個數,緊跟的後續nargout個參數y、out爲輸出參數,後續再剩下的多個參數x、in就爲輸入參數。

(3)注意事項:在VC中調用dll中的函數以前,須要先調用函數libFunInitialize初始化,在調用完函數之後,須要再調用函數libFunTerminate和mclTerminateApplication終止。

準備好了上述步驟,就能夠在VC中調用DLL中函數的了,簡單的示例代碼以下:

// include matlab sys head file
#include "mclmcr.h"
#include "matrix.h"
#include "mclcppclass.h"

// include lib head file
#include "libCaculateFun.h"

// link matlab sys lib
#pragma comment(lib, "mclmcrrt.lib")
#pragma comment(lib, "libmx.lib")
#pragma comment(lib, "libmat.lib")
#pragma comment(lib, "mclmcr.lib")

// link lib
#pragma comment(lib, "libCaculateFun.lib")

void TestDeno()
{
    // init lib
    if (!(libCaculateFunInitialize()))
    {
        std::cout<<"Could not init lib !"<<endl;
        return -1;
    }

    double xxxx[2= {0};
    double inin    = 0;

    double yyyy[2= {0};
    double outo    = 0;

    // 爲函數參數分配內存空間
    mwArray mwXX(12, mxDOUBLE_CLASS);
    mwArray mwIn(11, mxDOUBLE_CLASS);

    mwArray mwYY(12, mxDOUBLE_CLASS);
    mwArray mwOut(12, mxDOUBLE_CLASS);

    // 爲輸入參數賦值
    mwXX.SetData(&xxxx, 2);
    mwIn.SetData(&inin, 1);

    // 調用計算函數
    CaculateFun(2, mwYY, mwOut, mwXX, mwIn);

    // 獲取輸出參數
    outo = mwOut.Get(11);

    // lib Terminate
    libCaculateFunTerminate();

    // MCR Terminate
    mclTerminateApplication();
}

示例代碼經過VC調用Matlab DLL,將變量xxxx的值付給yyyy,將變量inin的值付給outo,相對簡單,就再也不詳細解釋,(注意這裏函數參數的賦值方式,後續還會說起)

 

3            Vc向Matlab函數傳遞參數

前面在介紹兩種混合編程的方式時,示例代碼中已經涉及到了參數的傳遞問題,對於VC中的簡單數據類型:變量、數組等,網上已經有不少資料介紹,前面示例代碼中也有所體現,這裏再也不詳細說明。

關於複雜數據結構:用戶自定義結構體類型,處理起來比較麻煩,網上能找到的資料也較少,因爲前面說起的兩種方式,在傳遞參數時,使用的數據類型是不一致的:引擎方式只能是mxArray類型,而DLL方式的參數只能是mwArray,所以須要分別介紹將結構體轉換成這兩種類型的方法:

先來定義兩個結構體:

// 三維座標系中的點
struct Postion
{
    double x;
    double y;
    double z;
};

// 名字標示的一個座標點
struct Coordinate
{
    struct Postion pos;
    char name[ARRAYSIZE];
};

分別介紹將如上兩個結構體轉成Matlab能使識別的Array的方法

 

(1)    結構體轉換成mxArray:解釋比較多餘,就直接上代碼吧:


mxArray *Staruct2mxArray(struct Postion *pStaruct)
{
    mxArray *pm, *mx;
    mwSize m = 1, n = 1;
    const int nfields = 3;
    const char *fieldnames[3= {"x","y","z"};
    const mwIndex index = 0;

    pm = mxCreateStructMatrix(m, n, nfields, fieldnames);

    mx = mxCreateNumericMatrix(m, n, mxDOUBLE_CLASS, mxREAL);
    memcpy(mxGetPr(mx), &(pStaruct->x), sizeof(double));

    mxSetFieldByNumber(pm, index, 0, mx);

    mx = mxCreateNumericMatrix(m, n, mxDOUBLE_CLASS, mxREAL);
    memcpy(mxGetPr(mx), &(pStaruct->y), sizeof(double));
    mxSetFieldByNumber(pm, index, 1, mx);

    mx = mxCreateNumericMatrix(m, n, mxDOUBLE_CLASS, mxREAL);
    memcpy(mxGetPr(mx), &(pStaruct->z), sizeof(double));
    mxSetFieldByNumber(pm, index, 2, mx);

    return pm;
}

mxArray *Staruct2mxArray(struct Coordinate *pStaruct)
{
    mxArray *pm, *mx;
    mwSize m = 1, n = 1;
    const int nfields = 2;
    const char *fieldnames[2= {"pos","name"};
    const mwIndex index = 0;

    pm = mxCreateStructMatrix(m, n, nfields, fieldnames);

    mx = Staruct2mxArray(&(pStaruct->pos));
    mxSetFieldByNumber(pm, index, 0, mx);

    n = ARRAYSIZE;
    mx = mxCreateNumericMatrix(m, n, mxCHAR_CLASS, mxREAL);
    memcpy(mxGetPr(mx), pStaruct->name, sizeof(char)*ARRAYSIZE);
    mxSetFieldByNumber(pm, index, 1, mx);

    return pm;
}

(2)    結構體轉換成mwArray:


mwArray Staruct2mwArray(struct Postion *pStaruct)
{
    mwSize m = 1, n = 1;
    const int nfields = 3;
    const char *fieldnames[3= {"x","y","z"};

    mwArray pm(m, n, nfields, fieldnames);

    mwArray mx(pStaruct->x);
    pm(fieldnames[0], 11= mx;

    mwArray my(pStaruct->y);
    pm(fieldnames[1], 11= my;

    mwArray mz(pStaruct->z);
    pm(fieldnames[2], 11= mz;

    return pm;
}

mwArray Staruct2mwArray(struct Coordinate *pStaruct)
{
    mwSize m = 1, n = 1;
    const int nfields = 2;
    const char *fieldnames[2= {"pos","name"};
    const mwIndex index = 0;

    mxArray pm(m, n, nfields, fieldnames);

    mxArray mpos = Staruct2mwArray(&(pStaruct->pos));
    pm(fieldnames[0], 11= mpos;

    mxArray mname(pStaruct->name);
    pm(fieldnames[1], 11= mname;

    return pm;
}

(3)    mxArray轉換成結構體:


struct Coordinate *mwArray2Staruct(mwArray pm)
{
   
 // 代碼僅處理 name 字段 pos 字段是三個簡單類型省略
    struct Coordinate *pStaruct = new struct Coordinate;

    const int nfields = 2;
    const char *fieldnames[2= {"pos","name"};

    mwArray mwStr = pm.Get(fieldnames[1], 11);
    char *str = strdup(mwStr.ToString());

    memcpy(pStaruct->name,str,sizeof(char)*ARRAYSIZE);

    return pStaruct;
}

(4)    mwArray轉換成結構體:


struct Coordinate *mxArray2Staruct(mxArray *pm)
{
   
 // 代碼僅處理 name 字段 pos 字段是三個簡單類型省略
    struct Coordinate *pStaruct = new struct Coordinate;

    mxArray * mxname = mxGetFieldByNumber(pm, 11)
    mxArray * str = mxGetPr(mxname);

    memcpy(pStaruct->name,str,sizeof(char)*ARRAYSIZE);

    return pStaruct;
}

相關文章
相關標籤/搜索